This post was written and submitted by Javier Calvarro Nelson, a developer on the ASP.NET Core MVC team
Testing is an important part of the development process of any app. In this blog post we’re going to explore how we can test ASP.NET Core MVC app using an in-memory server. This approach has several advantages:
- It’s very fast because it does not start a real server
- It’s reliable because there is no need to reserve ports or clean up resources after it runs
- It’s easier than other ways of testing your application, such as using an external test driver
- It allows testing of traits in your application that are hard to unit test, like ensuring your authorization rules are correct
For testing MVC app we’re going to use
TestServer is an in-memory implementation of a server for ASP.NET Core app akin to Kestrel or HTTP.sys.
Creating and setting up the projects
Start by creating an MVC app using the following command:
dotnet new mvc -au Individual -uld --use-launch-settings -o .\TestingMVC\src\TestingMVC
Create a test project with the following command:
dotnet new xunit -o .\TestingMVC\test\TestingMVC.Tests
Next create a solution, add the projects to the solution and add a reference to the app project from the test project:
dotnet new sln
dotnet sln add .\src\TestingMVC\TestingMVC.csproj
dotnet sln add .\test\TestingMVC.Tests\TestingMVC.Tests.csproj
dotnet add .\test\TestingMVC.Tests\TestingMVC.Tests.csproj reference .\src\TestingMVC\TestingMVC.csproj
Add references to the components we’re going to use for testing by adding the following item group to the test project file:
Now, we can run
dotnet restore on the project or the solution and we can move on to writing tests.
Writing a test to retrieve the page at ‘/’
Now that we have our projects set up, let’s wirte a test that will serve as an example of how other tests will look.
We’re going to start by changing Program.cs in our app project to look like this:
In the snippet above, we’ve changed the method
IWebHost BuildWebHost(string args) to call a new method
IWebHostBuilder CreateWebHostBuilder(string args) within it. The reason for this is that we want to allow our tests to configure the
IWebHostBuilder in the same way the app does and to allow making changes required by tests. (By chaining calls on the
One example of this will be setting the content root of the app when we’re running the server in a test. The content root needs to be based on the appliation’s root, not the test’s root.
Now, we can create a test like the one below to get the contents of our home page. This test will fail because we’re missing a couple of things that we describe below.
The test above can be decomposed into the following actions:
- Create an
IWebHostBuilderin the same way that my app creates it
- Override the content root of the app to point to the app’s project root instead of the bin folder of the test app. (.\src\TestingMVC instead of .\test\TestingMvc.Tests\bin\Debug\netcoreapp2.0)
- Create a test server from the
- Create an
HttpClientthat can be used to communicate with our app. (This uses an internal mechanism that sends the requests in-memory – no network involved.)
- Send an HTTP request to the server using the client
- Ensuring the status code of the response is correct
Requirements for Razor views to run on a test context
If we tried to run the test above, we will probably get an HTTP 500 error instead of an HTTP 200 success. The reason for this is that the dependency context of the app is not correctly set up in our tests. In order to fix this, there are a few actions we need to take:
- Copy the .deps.json file from our app to the bin folder of the testing project
- Disable shadow copying assemblies
For the first bullet point, we can create a target file like the one below and include in our testing project file as follows:
For the second bullet point, the implementation is dependent on what testing framework we use. For xUnit, add an xunit.runner.json file in the root of the test project (set it to Copy Always) like the one below:
This step is subject to change at any point; for more information look at the xUnit docs at http://xunit.github.io/#documentation.
Now if you re-run the sample test, it will pass.
- We’ve seen how to create in-memory tests for an MVC app
- We’ve discussed the requirements for setting up the app to find static files and find and compile Razor views in the context of a test
- Set up the content root in the tests to the app’s root folder
- Ensure the test project references all the assemblies in the app
- Copy the app’s deps file to the bin folder of the test project
- Disable shadow copying in your testing framework of choice
- We’ve shown how to write a functional test in-memory using
TestServerand the same configuration your app uses when running on a real server in Production
The source code of the completed project is available here: https://github.com/aspnet/samples/tree/master/samples/aspnetcore/mvc/testing/TestingMVC