Implementing Functional Testing in MVC Application using ASP.NET Core 2.1.0-preview1

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

  1. Manual copying of the .deps files from application project into the bin folder of the test project
  2. Needed to manually set the content root of the application project so that static files and views can be found
  3. 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 

  1. Add the reference for the application project against which we are writing test methods
  2. 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<TStartup> : WebApplicationTestFixture<TStartup> 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<WebAppFuncTestFixture<Startup>>
    {
        public WebAppFuncTests(WebAppFuncTestFixture<Startup> 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 built in project template, it had already created that method in our Program.cs class. As you see from the below snippet we already have a static method named CreateWebHostBuilder which will be used the test framework to bootstrap the application in the memory

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<Startup>();
    }
}

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(WebAppFuncTestFixture fixture)
        {
            Client = fixture.Client;
            Client.BaseAddress = new Uri("https://localhost");
        }

If we run the tests now, the test should pass as shown below


No Comments

Add a Comment