Search code examples
c#asp.net-coreintegration-testingxunitfixtures

Creating WebApplication fixture for integration tests in NET7 throws


I want to create WebApplication fixture so I can share it among my integration tests. That's how it looks:

public class WebAppFixture : IDisposable
{
    private readonly TestServer _server;

    const string WsHubPath = "/";

    public WebAppFixture()
    {
        var builder = WebApplication.CreateBuilder();

        builder.Services.AddUltraWs()
            .AddWsHub<TestWsHub>();

        var app = builder.Build();

        app.UseWebSockets();
        app.MapUltraWs<TestWsHub>(WsHubPath);

        _server = app.GetTestServer();
        HttpClient = app.GetTestClient();
    }

    public HttpClient HttpClient { get; }
    public Uri WsHubUri => new Uri(_server.BaseAddress + WsHubPath);
    public WebSocketClient CreateWebSocketClient() => _server.CreateWebSocketClient();

    public void Dispose()
    {
        _server.Dispose();
        HttpClient.Dispose();
    }
}

But when I want to use it in my test class:

public class WsHubTests : IClassFixture<WebAppFixture>
{
    private readonly WebAppFixture _webAppFixture;

    public WsHubTests(WebAppFixture webAppFixture)
    {
        _webAppFixture = webAppFixture;
    }
}

Then I get following exception:

System.AggregateException : One or more errors occurred. (Unable to cast object of type 'Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl' to type 'Microsoft.AspNetCore.TestHost.TestServer'.) (The following constructor parameters did not have matching fixture data: WebAppFixture webAppFixture) ---- System.InvalidCastException : Unable to cast object of type 'Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl' to type 'Microsoft.AspNetCore.TestHost.TestServer'. ---- The following constructor parameters did not have matching fixture data: WebAppFixture webAppFixture

How to fix it? What am I doing wrong?


Solution

  • Your problem does not lie with the IClassFixture<WebAppFixture> and the accompanying constructor parameter but with what happens in the WebAppFixture when it's constructed. A hint to what is going wrong is given in the error you have posted.

    (Unable to cast object of type 'Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl' to type 'Microsoft.AspNetCore.TestHost.TestServer'.)

    If you put a breakpoint in the WebAppFixture constructor and try to step over the line where you assign the result of GetTestServer to _server you will get an unhandled exception

    enter image description here

    This is because you are using WebApplication.CreateBuilder() instead of WebApplicationFactory<TEntrypoint>. So if we check integration testing examples from microsoft for some examples and modify them slightly to accommodate your situation.

    public class WebAppFixture : IDisposable
    {
        public readonly HttpClient TestServerClient;
    
        public WebAppFixture()
        {
            TestServerClient = new WebApplicationFactory<Program>()
                .WithWebHostBuilder(builder =>
                {
                    builder.ConfigureAppConfiguration((_, context) =>
                    {
                        //context.AddInMemoryCollection();
                    });
                    builder.ConfigureTestServices(services =>
                    {
                        //services.AddTransient<SomeService>();
                    });
                    builder.UseEnvironment("LOCAL_TEST");
                }).CreateClient();
        }
    
        public void Dispose()
        {
            TestServerClient.Dispose();
        }
    }
    

    Your test class is unchanged and you can test your implementation using _webAppFixture.TestServerClient

    [Fact]
    public async Task TestSomething()
    {
        var result = await _webAppFixture.TestServerClient.GetAsync("/WeatherForecast");
        IEnumerable<WeatherForecast> autoParsedResult = await _webAppFixture.TestServerClient.GetFromJsonAsync<IEnumerable<WeatherForecast>>("/WeatherForecast");
    
        Assert.Equal(System.Net.HttpStatusCode.OK, result.StatusCode);
        Assert.Equal(5, autoParsedResult.Count());
    }
    

    Links you want to take a look at: