Industry Insights, Information, and Developer News
Integration testing is a crucial step in ensuring that your application behaves correctly when different components interact with one another. In ASP.NET Core, the WebApplicationFactory<TEntryPoint> class makes it easier to test your web applications by allowing you to spin up an instance of your app in-memory and run integration tests against it.
WebApplicationFactory<TEntryPoint>
As a new author contributing to the VSLive! Blog, in my inaugural post I'll walk you through how to use WebApplicationFactory for integration testing, using a simple ASP.NET Core web application. We'll write tests to ensure that the Home page is functioning as expected. Let's dive in!
WebApplicationFactory
Structure of the Sample ProjectThis post includes a code sample with "before" and "after" versions of the code.
In the solution shown above we have a total of four projects. We have two main projects:
Additionally, we have two test projects:
We'll focus on the Integration Tests project, using WebApplicationFactory to create tests that check if the web application behaves correctly in real-world scenarios.
What Is WebApplicationFactory? WebApplicationFactory<TEntryPoint> is a handy class in ASP.NET Core that allows us to spin up our application in-memory for testing purposes. This eliminates the need for dealing with network ports or external dependencies during testing. By creating an in-memory version of the application, we can run HTTP requests against it, similar to how we would interact with the deployed app.
Creating an Integration Test Let's create a test to verify that the Home page of our web application displays the text "Hello World." To get started, open your Integration Test project, right-click, and add a new class. We'll name it HomeControllerFixture.
HomeControllerFixture
Writing the Test -- Here's the basic structure of our test:
public async void IndexContainsHelloWorld() { // arrange var factory = new WebApplicationFactory<JustAnEmptyClass>(); var client = factory.CreateClient(); // act var response = await client.GetAsync("/"); // assert Assert.NotNull(response); var content = await response.Content.ReadAsStringAsync(); Assert.Contains("Hello, world!", content); Assert.True(response.IsSuccessStatusCode); }
Breakdown of the Test:
WebApplicationFactory<JustAnEmptyClass>
CreateClient()
HttpClient
client.GetAsync("/")
GET
EnsureSuccessStatusCode()
Assert.Contains()
Handling the Missing Startup Class You might notice that we reference a class called JustAnEmptyClass instead of the Startup class. Here's why: In older ASP.NET Core projects, the Startup.cs file was used to configure the application. However, in newer project templates, the Program.cs file has taken over this role, combining the functionality of both Program and Startup. This is all part of the change to add "top-level statements" to C#. On the one hand, it simplifies the code. On the other hand, it sometimes gets in the way, and this is one of those cases where it gets in the way.
Startup
JustAnEmptyClass
Program
If your project actually has a Startup.cs class, then you can use that (WebApplicationFactory) instead of creating this empty, marker class. Or if you'd like to have your project actually use that style of project you can easily do that by adding the --use-program-main option to your dotnet new command when you create your project.
Startup.cs
--use-program-main
dotnet new
Unfortunately, WebApplicationFactory doesn't work directly with the Program class. The workaround is simple: create an empty class and use it as a placeholder for WebApplicationFactory. This doesn't interfere with the functionality of your application but allows you to run your tests.
// JustAnEmptyClass.cs public class JustAnEmptyClass { // This class is intentionally left empty. }
Now, when creating the factory, we reference JustAnEmptyClass instead of Startup or Program.
Running the Tests: It Fails After writing the test, run it from your Test Explorer window. If you encounter an error that the test is missing, check that you've added the [Fact] attribute to the test method -- this tells the test framework that it's a test that should be run.
[Fact]
Upon running the test, it should fail initially because the home page doesn't yet contain the string "Hello World."
Fixing the Failure In our sample application, fixing this problem is easy enough. There's nothing on the home page that says "hello world" and therefore we just need to add that. In real-life development situations, you'll probably be testing for more complex things and you might need to write some real code.
To fix this, let's add "Hello World" to our Home page.
When you've made the changes to Index.cshtml, it should look something like this:
This time, when you run the tests, the test should pass, confirming that our integration test successfully verifies the content of the home page.
Conclusion Using WebApplicationFactory, we can easily write integration tests that spin up an in-memory version of our ASP.NET Core application. This allows us to test the interaction between controllers, views, and other components without needing to deploy the app to a server.
In this demo, we wrote a test to check that the Home page contains "Hello World," but you can extend this approach to test more complex scenarios like API endpoints, form submissions and more.
Posted by Benjamin Day on 09/23/2024