22/12/21 22:40 Functional testing for ASP.
NET Core API | by Andrew Kulta | Medium
Get started Open in app
Andrew Kulta
200 Followers About Follow
You have 2 free member-only stories left this month. Sign up for Medium and get an extra one
Functional testing for ASP.NET Core API
Andrew Kulta Oct 16 · 2 min read
Functional testing is next level above the unit tests and gives us confidence that code
which we wrote works as it intended to. If we talking about functional testing in scope
of asp.net we mean a way how we test our API.
What actually functional testing is?
Functional testing is the process through which we determine if a piece of software
is acting in accordance with pre-determined requirements. It uses black-box testing
techniques, in which the tester has no knowledge of the internal system logic.
In scope of API we need to prepare possible requests which we expect to get and be
sure that
Should we even spend time for it?
Definitely! If you don`t write integrational tests than functional testing is a solution to
be confident that you code has a bit fewer bugs that it could have. When you have
ability to write integrational tests than for your it should be simple to reuse common
code base for functional and for integrational tests. One difference will be in proper
environmental setup.
Let define simple API
https://medium.com/@kylia669/functional-testing-for-asp-net-core-api-4318df89311a 1/6
22/12/21 22:40 Functional testing for ASP.NET Core API | by Andrew Kulta | Medium
In test purposes out controller contains only single action to create some entity. it
Get started Open in app
could be any entity which required by our business rules. Besides we should
remember about authentication nowadays all APIs are protected by some kind of
authentication. Let`s assume that current API requires Authorization header with
Bearer token.
1 [ApiController]
2 public class MyController : ControllerBase
3 {
4 private readonly IService _service;
5
6 public MyController(IService service)
7 {
8 _service = service;
9 }
10
11 [HttpPost("entities")]
12 [ProducesResponseType(typeof(CreateResponse), (int)HttpStatusCode.OK)]
13 [ProducesResponseType(typeof(ExceptionResponse), (int)HttpStatusCode.BadRequest)]
14 public Task<CreateResponse> CreateAsync([FromBody] CreateRequest request)
15 {
16 return _service.CreateAsync(request);
17 }
18 }
ApiController.cs
hosted with ❤ by GitHub view raw
How will functional test look like for this controller ?
In my test project I used NUnit as test framework but it s up to you to choose which
one better suites for you needs. NUnit has all required functionality to write good
tests also it`s quite popular on the market.
Nuget packages which should be included in test project are:
dotnet add package NUnit
dotnet add package NUnit3TestAdapter
1 [TestFixture]
2 public class ApiControllerTests
3 {
4 private const string CreateUrl = "/entities";
5
https://medium.com/@kylia669/functional-testing-for-asp-net-core-api-4318df89311a 2/6
22/12/21 22:40 Functional testing for ASP.NET Core API | by Andrew Kulta | Medium
6 private readonly TokenBuilder _tokenBuilder = new();
Get
7 started Open in app
8 private TestServer? _server;
9 private HttpClient? _client;
10
11 [SetUp]
12 public void Setup()
13 {
14
15 var settings = new Settings
16 {
17 SymmetricFuncTestKey = "your key for functional tests"
18 };
19
20 var builder = new WebHostBuilder()
21 .UseStartup<Startup>()
22 .ConfigureTestServices(services =>
23 {
24 // Mock your external services here
25 })
26 .ConfigureServices(services =>
27 {
28 // Change authentication to use bearer token which signed by symmetric ke
29 services.PostConfigure<JwtBearerOptions>(JwtBearerDefaults.Authentication
30 {
31 options.TokenValidationParameters = new TokenValidationParameters
32 {
33 IssuerSigningKey =
34 new SymmetricSecurityKey(Encoding.UTF8.GetBytes(settings.Symm
35 ValidateIssuerSigningKey = true,
36 ValidateIssuer = true,
37 ValidateAudience = true,
38 ValidateLifetime = true,
39 ValidIssuer = TestConstants.Issuer,
40 ValidAudience = TestConstants.Audience,
41 };
42 });
43 });
44 _server = new TestServer(builder);
45 _client = _server.CreateClient();
46 }
47
48 [Test]
49 public async Task CreateAsync_Should_ReturnNotAuthenticated_When_RequestWithoutAuthTo
50 {
51 // Arrange
52 var request = new CreateRequest
53 {
https://medium.com/@kylia669/functional-testing-for-asp-net-core-api-4318df89311a 3/6
22/12/21 22:40 Functional testing for ASP.NET Core API | by Andrew Kulta | Medium
54 PropertyName = "propertyValue"
Get
55
started };Open in app
56 var json = JsonConvert.SerializeObject(request);
57 using var data = new StringContent(json, Encoding.UTF8, "application/json");
58
59 // Act
60 var response = await _client?.PostAsync(CreateUrl, data)!;
61
62 // Assert
63 response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
64 }
65
66 [Test]
67 public async Task CreateAsync_Should_Return400_When_RequestValidationFailed()
68 {
69 // Arrange
70 var request = new CreateRequest();
71 var json = JsonConvert.SerializeObject(request);
72 using var data = new StringContent(json, Encoding.UTF8, "application/json");
73 var token = await _tokenBuilder.BuildJWTToken();
74 _client?.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
75
76 // Act
77 var response = await _client?.PostAsync(CreateUrl, data)!;
78
79 // Assert
80 response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
81 }
82
83 [Test]
84 public async Task CreateAsync_Should_ReturnCreateResponse()
85 {
86 // Arrange
87 var request = new CreateRequest
88 {
89 PropertyName = "propertyValue"
90 };
91 var json = JsonConvert.SerializeObject(request);
92 using var data = new StringContent(json, Encoding.UTF8, "application/json");
93 var token = await _tokenBuilder.BuildJWTToken();
94 _client?.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
95
96 var expected = new CreateResponse
97 {
98 PropertyName = "propertyValue"
99 };
100
101 // Act
https://medium.com/@kylia669/functional-testing-for-asp-net-core-api-4318df89311a 4/6
22/12/21 22:40 Functional testing for ASP.NET Core API | by Andrew Kulta | Medium
101 // Act
102 var response = await _client?.PostAsync(CreateUrl, data)!;
Get started Open in app
103
104 // Assert
105 response.StatusCode.Should().Be(HttpStatusCode.OK);
106 var responseBody = await response.Content.ReadAsStringAsync();
107 var responseObject = JsonConvert.DeserializeObject<CreateResponse>(responseBody);
108 responseObject.Should().BeEquivalentTo(expected);
109 }
110 }
We should cover that API endpoint protected by authentication and clients can`t
request it without proper token. In order to do this we need to prepare test server
with proper authentication setup. To do it in setup we need to override
JwtBearerOptions with our test authentication which relies on auth token signed by
SymmetricSecurityKey. To be compliant with security rules we should avoid to store
any secrets in code and I don`t suggest you to violate this rule even for test project.
When configuration for authentication is ready, in testcases proper token should be
generated to proceed to business logic execution. We can separate token generation
into TokenBuilder and use it when we need to get auth token.
1 public static class TestConstants
2 {
3 public static string Audience { get; set; } = "Audience";
4
5 public static string Issuer { get; set; } = "Issuer";
6 }
7
8 public class TokenBuilder
9 {
10 public string BuildJWTToken()
11 {
12 var settings = new Settings
13 {
14 SymmetricFuncTestKey = "your key for functional tests"
15 };
16 var privateKey = settings.SymmetricFuncTestKey;
17 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(privateKey));
18 var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
19 var jwtValidity = DateTime.UtcNow.AddMinutes(5);
20
21 var token = new JwtSecurityToken(
22 TestConstants.Issuer,
https://medium.com/@kylia669/functional-testing-for-asp-net-core-api-4318df89311a 5/6
22/12/21 22:40 Functional testing for ASP.NET Core API | by Andrew Kulta | Medium
23 TestConstants.Audience,
Get started
24 Open in appjwtValidity,
expires:
25 signingCredentials: creds,
26 claims: new List<Claim>
27 {
28 new (ClaimTypes.Role, "owner"),
29 });
30
31 return new JwtSecurityTokenHandler().WriteToken(token);
32 }
33 }
TokenBuilder.cs
hosted with ❤ by GitHub view raw
In conclusion I hope you get feeling that functional tests are quite simple and it allows
to verify you API each time when something changed. Functional tests could be
included as a step in your build pipeline. It gives to even to confidence that
everything works good before deploying to production.
Aspnetcore C Sharp Programming Functional Testing Test Automation Api Testing
About Write Help Legal
Get the Medium app
https://medium.com/@kylia669/functional-testing-for-asp-net-core-api-4318df89311a 6/6