In this repository you can find a .NET WebAPI and a test project, using the built-in TestServer
and WebApplicationFactory
to write integration tests against your HTTP endpoints. One of the example of using a library called Alba, which utilizes the built-in TestServer
.
Authentication can causes unauthorized response in the integration test. The following solutions can be used.
Separate branch with the .NET Core 2.2 version.
- Integration tests in ASP.NET Core 📚
- Converting integration tests to .NET Core 3.0 📓Andrew Lock
- Identity user accounts in integration tests using
ActionFilter
📓Gunnar Peipman - Feature: Validate configurations on application start
- Adding validation to the options pattern 📓Milan
- Using .NET 8 Source Generators to validate IOptions during startup 📓Code-Maze - OptionsValidator attribute
- Best way to validate your settings (DataAnnotation, FluentValidation) 📽️18m - Nick Chapsas
- Validating nested DataAnnotation IOptions recursively with MiniValidation 📓Andrew Lock
- GSoft.ComponentModel.DataAnnotations 👤GSoft - Provides new data annotation attributes
- Adding validation to strongly typed configuration objects using FluentValidation 📓Andrew Lock
- Options Validation 📓Code-Maze
- Validating Connection Strings on Startup 📓Khalid Abuhakmeh
- Using
WebHostBuilder
to create aTestServer
manually. - Using a
TestStartup
class derived fromStartup
. - Apply a custom
AuthenticationHandler
to authorize the request. Authorize
attribute affects the response (200, 401, 403).
IWebHostBuilder builder = new WebHostBuilder()
.UseStartup<TestStartup>();
_testServer = new TestServer(builder);
Client = _testServer.CreateClient();
- Using
WebApplicationFactory
and override theConfigureWebHost
method. - Using the same authentication mechanism which is defined in the
Startup
file. - After .NET Core 3, there is an AuthorizationMiddleware using a PolicyEvaluator which makes this solution a bit different compared with the previous version.
- Cons:
Authorize
attribute in the controller does not have any effects for the response.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services
.AddSingleton<IValueProvider, FakeValueProvider>()
.AddSingleton<IPolicyEvaluator>(_ => new FakeUserPolicyEvaluator(() => TestUser?.ToClaims()));
});
}
- Using
WebApplicationFactory
and override theConfigureWebHost
method. - Using the
Startup
file, but override the authentication mechanism with the customAuthenticationHandler
from the Solution #1. Authorize
attribute effects the response (200, 401, 403).
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.AddSingleton<IValueProvider, FakeValueProvider>();
services.AddTestAuthentication(o => o.TestUserClaimsFunc = () => TestUser?.ToClaims());
});
}
Using a library called Alba which utilizes the built-in TestServer
public class AlbaHostFixture
{
public IAlbaHost AlbaWebHost { get; set; }
public UserModel TestUser { get; set; }
public async Task InitializeAsync()
{
AlbaWebHost = await AlbaHost.For<Program>(configureWebHostBuilder);
}
private void configureWebHostBuilder(IWebHostBuilder webHostBuilder)
{
webHostBuilder.ConfigureTestServices(configureTestServices);
}
private void configureTestServices(IServiceCollection services)
{
services.AddTestAuthentication(configureAuthOptions);
services.AddSingleton<IValueProvider, FakeValueProvider>();
}
private void configureAuthOptions(TestAuthenticationOptions options)
{
options.TestUserClaimsFunc = () => TestUser?.ToClaims();
}
}
- Another way to bypassing the JWT authentication process in my repository: PlayingWithSignalR.
- Apply a custom
DelegatingHandler
in theCreateDefaultClient
method. - The handler injects a token in the
Authorization
header.
public class WebApiFactory : WebApplicationFactory<Startup>
{
public WebApiFactory()
{
HttpClient = CreateDefaultClient(new AuthDelegatingHandler(...));
}
}
public class AuthDelegatingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(...)
{
request.Headers.Authorization = ...;
return base.SendAsync(request, cancelToken);
}
}