Enhance your .NET Testing #1: WebApplicationFactory

The WebApplicationFactory class in .NET provides a powerful tool for integration testing. It allows you to create a factory for bootstrapping an application in memory for testing.

This article provides an overview of how to use WebApplicationFactory to create end-to-end tests using the xUnit ClassData attribute.

What is WebApplicationFactory ?

Before going any further, let's find out what WebApplicationFactory is used for.

Factory for bootstrapping an application in memory for functional end to end tests.
– Microsoft documentation

WebApplicationFactory is used to create an instance of your web application in memory. This is beneficial for end to end tests, as it allows you to exercise the full stack of your application, from the HTTP request all the way down to your database or external services.

One of its great advantages is that it's already in the ASP.NET Core package, so there's no need to install a third-party package to use this feature. The complete namespace for it is Microsoft.AspNetCore.Mvc.Testing.

This means that developments are maintained by the dotnet community and are unlikely to become obsolete in the next few years.

Creating an instance of the factory class is as simple as this:

private readonly WebApplicationFactory<Startup> factory = new WebApplicationFactory<Startup>();

However, starting from .NET 6, the Startup class has essentially merged into the Program class, which is automatically generated from the top-level statements in the Program.cs file.

Since WebApplicationFactory use the Program.cs, it will load the same configuration used to start the application. This way, there's no need to duplicate the appsettings.json file in the test project.

However, as this is a web application, it is possible to override the configuration. In the case of tests, we often create fakes to remove the complexity of a class or dependency on an external component. It can also happen that the application configuration contains elements that refer to components we don't want to keep (such as an external link to a Redis cache or another API).

The ConfigureWebHost method is used to override the configuration.

internal class GoatWebApplication : WebApplicationFactory<Program>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        Environment.SetEnvironmentVariable("CacheSettings:UseCache", "false");

        _ = builder.ConfigureTestServices(services =>
        {
            services.AddTransient<Interface, ImplementationFake>();
        });
    }
}

A simple test could look like this:

[Fact]
public async Task ShoudReturns200_WhenGetValidPath()
{
	// Arrange
    await using var application = new GoatWebApplication();
    var client = application.CreateClient();
    
    // Act
    var response = await client.GetAsync("/validpath");

	// Assert
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

In this example, we're creating an instance of a test application that extends WebApplicationFactory<Program>. We're then using this to create a client and send a GET request to the "/validpath" endpoint. The test asserts that the HTTP response status code is OK (200)

This first use of WebApplicationFactory is interesting, and we can go further to make our tests data-driven.

Combine it with xUnit attributes

By combining WebApplicationFactory and the xUnit ClassData attribute, you can create robust end-to-end tests that cover a wide range of scenarios. You can use WebApplicationFactory to create an instance of your application and simulate HTTP requests, and then use ClassData to provide different sets of data for your tests.

This enables you to test how your application handles different inputs and scenarios, improving the quality of your tests and helping to catch any potential bugs or issues.

Before starting, if you need to refresh your memory, I suggest you take a quick look at the xUnit attributes

Create efficient C# tests with the xUnit library
Among all C# test libraries, xUnit is one of the most used by .NET developers. It’s simple to use, and that’s the subject of this post: How to use xUnit attributes to create powerful tests? Imagine a parallel world, where goats are brilliant developers, who start learning in school to

Here's an example of how you might do this:

internal class GoatWebApplication : WebApplicationFactory<Program>, IEnumerable<object[]> 
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        Environment.SetEnvironmentVariable("CacheSettings:UseCache", "false");

        _ = builder.ConfigureTestServices(services =>
        {
            services.AddScoped<Interface, ImplementationFake>();
        });
    }

    public IEnumerator<object[]> GetEnumerator()
    {
        var client = CreateClient();
        
        yield return new object[]
        {
        	client,
            "validpath"
            200,
        };

        yield return new object[]
        {
        	client,
            "invalidpath"
            404,
        };
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

GoatWebApplication now also inherits from IEnumerable<object[]> to conform to ClassData.

The enumerator will return 2 use cases:

  • One with a valid path returning an HTTP 200 code
  • The other with an invalid path returning an HTTP 404 code

As expected, the test is much smaller, with very little internal logic. It simply makes the call and implements the validation defined in the ClassData

[Theory]
[ClassData(typeof(LegacyV1TestWebApplication))]
public async Task ShoudReturnsExpectedHttpCode_WhenGetPath(HttpClient client, string path, int expectedStatusCode)
{
	// Arrange

	// Act
    var response = await client.GetAsync(path);

	// Assert
    Assert.Equal(expectedStatusCode, response.StatusCode);
}
📢
If you're concerned about test performance, you can pass the WebApplicationFactory in a fixture called once for your test class instead of once per test, as shown here

Summary


Using WebApplicationFactory with xUnit's ClassData attribute offers key advantages for end-to-end testing in .NET:

  1. Full Stack Testing: WebApplicationFactory facilitates testing of the entire application stack, simulating real-world behavior more accurately than testing individual components.
  2. Flexible Input: ClassData provides a wide range of test inputs, increasing the comprehensiveness and flexibility of testing.
  3. Simplified Configuration: WebApplicationFactory simplifies the setup of in-memory test server and client, making tests easier to write and maintain.
  4. Test Isolation: Each WebApplicationFactory instance is isolated, enabling parallel testing without interference, improving reliability and performance.
  5. Realistic Environment: WebApplicationFactory includes actual startup and configuration code, increasing the likelihood of catching issues that unit tests might miss.
  6. Dynamic Behavior Control: ClassData allows for dynamic control over test behavior, making it easier to test varying scenarios.

These benefits contribute to more robust and reliable end-to-end tests for .NET applications.

Next article:

Enhance your .NET Testing #2: InMemoryDatabase
The InMemoryDatabase offers an efficient avenue for performing tests that need database interactions without the overhead of database operations.

To go further, you can read the Microsoft documentation :

Integration tests in ASP.NET Core
Learn how integration tests ensure that an app’s components function correctly at the infrastructure level, including the database, file system, and network.

Have a goat day 🐐