Functional Testing plays an important role in delivering software products with great quality and reliability. Even though the ability to write in-memory functional tests in ASP. NET Core MVC application is present in ASP.NET Core 2.0, it had some pitfalls
- Manual copying of the .deps files from application project into the bin folder of the test project
- Needed to manually set the content root of the application project so that static files and views can be found
Bootstrapping the app on the Test Server.
In ASP.NET Core 2.1, Microsoft has released a new package Microsoft.AspNetCore.Mvc.Testing which solves all the above mentioned problems and helps you to write and execute in-memory functional tests more efficiently. To check how it can be done, let's create two projects - one for the web application and another for our test project.
Step 1
Create an ASP.NET Core MVC project without any authentication. The following command will create one in the folder specified by the -o switch
dotnet new mvc -au none -o SampleWeb/src/WebApp
Step 2
Let's add a test project based on Xunit test framework using the below command. As in Step 1, the project will be created in the specified folder
dotnet new xunit -o SampleWeb/test/WebApp.Tests
Step 3
Now we will create a solution file and add these two projects into it.
cd SampleWeb
dotnet new sln
dotnet sln add src/WebApp/WebApp.csproj
dotnet sln add .\test\WebApp.Tests\WebApp.Tests.csproj
Step 4
Before we get started with writing test methods, we will need to add some references
- Add the reference for the application project against which we are writing test methods
- Add the NuGet package Microsoft.AspNetCore.Mvc.Testing which is a new one released in version 2.1.0
dotnet add .\test\WebApp.Tests\WebApp.Tests.csproj reference .\src\WebApp\WebApp.csproj
dotnet add .\test\WebApp.Tests\WebApp.Tests.csproj package Microsoft.AspNetCore.Mvc.Testing -v 2.1.0-preview1-final
Step 5
Create a test fixture Class in the test project with the following contents.
using Xunit; using Xunit; using Microsoft.AspNetCore.Mvc.Testing; namespace WebApp.Tests { public class WebAppFuncTestFixture: WebApplicationTestFixture where TStartup : class { public WebAppFuncTestFixture(): base("src/WebApp") { } } }
By default, the framework will search for the content root in the pattern given below
SampleWeb
-> WebApp.sln
-> WebApp -> WebApp.csproj
-> WebApp.Tests -> WebApp.Tests.csproj
But our structure is different and as of now the preview version can't automatically identify the content root, so will need to pass the relative path in the base constructor in our Test Fixture class
Step 6
Now we will create another class in the test project for writing the test method. The following content has one test method which is written against the index method in the application project
public class WebAppFuncTests : IClassFixture> { public WebAppFuncTests(WebAppFuncTestFixture fixture) { Client = fixture.Client; } public HttpClient Client { get; } [Fact] public async Task GetHomePage() { // Arrange & Act var response = await Client.GetAsync("/"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } }
In the code that we are using a HttpClient object for initiating the web request for retrieving the home page, but all the bootstrapping needed for setting up it is now handled by the new package itself and allows you to invoke the app in memory.
Basically, the Test Fixture class will look for a static method on the entry point class in your application project. Since we used the
namespace WebApp { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup(); } }
Step 7
That's all we need to run the test and let's do it by using the below command
dotnet test .\test\WebApp.Tests\WebApp.Tests.csproj
But when you run the tests, you will get the output as given below. The tests have failed because in 2.1 the web application enforces HTTPS by default, but in the preview version the Test Fixture class is still using HTTP for the base address and that's why we got a Temporary Redirect status instead of 200 OK.
So to make it run successfully we need to add a single line of code in our test class. We will modify the constructor to specify the base address with https when initiating the request for the homepage
public WebAppFuncTests(WebAppFuncTestFixturefixture) { Client = fixture.Client; Client.BaseAddress = new Uri("https://localhost"); }
If we run the tests now, the test should pass as shown below