Simplifying integration testing with .NET and Playwright
In my earlier blog, Integration Testing with Playwright and Testcontainers, I shared how to use a WebApplicationFactory with TestContainers and Playwright to set up integration tests. While this approach works well, I encountered a problem: the setup inadvertently spins up two separate hosts, leading to unwanted behaviour.
Nwwz.Mvc.Testing
I am introducing NuGet package Nwwz.Mvc.Testing to solve those issue.It’s a simple helper to easily setup a .NET application fro integration testing while allowing a browser to connect to it.
The problem
The solution from my previous blog relies on the use of a CustomWebApplicationFactory with some additional code in it to allow the browser to access the application under test:
protected override IHost CreateHost(IHostBuilder builder)
{
// Create a plain host that we can return.
var dummyHost = builder.Build();
// Configure and start the actual host.
builder.ConfigureWebHost(webHostBuilder => webHostBuilder.UseKestrel());
var host = builder.Build();
host.Start();
return dummyHost;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseUrls("https://localhost:7048");
....
}
In short this means that not one but two hosts are created. One TestServer
which is baked into Microsoft’s WebApplicationFactory and a normal IServer
due to the Createhost()
that uses Kestrel internally. While this might be an issue from resource perspective this also two other side effects.
ConfigureWebHost is called twice
Both TestServer
and Createhost()
make a call to ConfigureWebHost()
. This means that that method is called twice. So if there are things in there that cannot run twice, that will be a problem.
CreateClient()
CreateClient()
returns a HttpClient that allows interaction with the host created by TestServer
, while the Kestrel host listens on the network. These hosts don’t share anything so data that is changed via the API using the HttpClient is not visible in the UI that is accessed through the Kestrel endpoint.
Although the issue with CreateClient()
can be solved as describe in this article described in this article. That solution still instantiates a TestServer
.
Solution
The solution is to have a WebApplicationFactory that spins up Kestrel and does not spin up a TestServer. That is exactly what Nwwz.Mvc.Testing does.
This package provides an alternative implementation of Microsoft’s WebApplicationFactory. It makes a single call to ConfigureWebHost()
and starts Kestrel to expose the application to the network. CreateClient()
still returns a client that connects to the application under test. Additionally the server address is now exposed via Property ServerAddress
for it to be used by frameworks like Playwright.
To use simply install the package:
dotnet add package Nwwz.Mvc.Testing
The code is forked from Microsoft.AspNetCore.Mvc.Testing and can be found at https://github.com/netwatwezoeken/Mvc.Testing
I have also updated the example that I used before: https://github.com/netwatwezoeken/full-integration-testing/tree/main
Compatible with additional features
This new packages is completely compatible with Microsoft.AspNetCore.Mvc.Testing. The derived class that you might already have (e.g. a CustomWebApplicationFActory
) still works and CreateClient()
will still return a client that connects to the application under test.
Random port
Each time Kestrel is started an available port is selected. This allows parallelization of tests.
Https
By default https is used. To disable https and use http instead set ‘UseHttps’ to false:
internal class CustomWebApplicationFactory : Nwwz.Mvc.Testing.WebApplicationFactory<Program>
{
public CustomWebApplicationFactory()
{
UseHttps = false;
}
}
Help, remarks and questions
As always I’m happy to to answer questions and get feedback.
I help organisations and developers to build better software faster. Feel free to reach out if you want to know more.