/PlayingWithTestHost

Try out the built-in TestServer to write integration tests and bypassing the authentication.

Primary LanguageC#

Playing with TestHost

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.

Resources

Solution #1

  • Using WebHostBuilder to create a TestServer manually.
  • Using a TestStartup class derived from Startup.
  • 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();

Solution #2

  • Using WebApplicationFactory and override the ConfigureWebHost 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()));
    });
}

Solution #3

  • Using WebApplicationFactory and override the ConfigureWebHost method.
  • Using the Startup file, but override the authentication mechanism with the custom AuthenticationHandler 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());
    });
}

Solution #4

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();
    }
}

Solution #5

  • Another way to bypassing the JWT authentication process in my repository: PlayingWithSignalR.
  • Apply a custom DelegatingHandler in the CreateDefaultClient 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);
    }
}