ASP.NET Core – MVC xUnit Integration Testing

Recently I wanted to test my new ASP.NET Core MVC app, so that I can be sure my startup.cs configuration is correct, and especially focusing on the JSON parsing of it.

I straight away run into a few basic stumbling blocks that might help a few people out there!

Test Discovery

The first stumbling block was trying to get the tests discovered at all.

I had to select a test runner and add this to the root of my project.json:

{
  "testRunner": "xunit",
  ....
}

I tried to initially use xunit and dotnet-test-xunit versions 2.1.0 which is the officially released version at the time of writing.

But as this does not support Microsoft.NETCore.App 1.0.1, after a bit off Bing’ing around, I figured out that I need to use "dotnet-test-xunit": "2.2.0-preview2-build1029"

Referencing the MVC Project

The second stumbling block was trying to simply reference the MVC project, in this case, “Taskily”. So I added the following line:

"dependencies": {
  .....
  "Taskily": "1.0.0*"
}

But this was giving me a “runtimes” is an unsupported framework error, which I couldn’t for the life of me figure out. In the end, it turned out to be that because the Taskily project (or at least it was called Taskily in the .sln) actually resided in a folder called WebApplication1 so my solution structure looked like this

root
|global.json
|---src
    |---WebApplication1  <-- renamed to Taskily in .sln
    |---Taskily.Tests    <-- new test project

The way I got this to compile is

  1. Close the .sln
  2. Rename WebApplicaiton1 folder to Taskily (same as project name in .sln)
  3. Re-Open .sln (Taskily won’t load as it is looking for src/WebApplication1/Taskily.xproj
  4. Remove the reference to the Taskily project from the .sln.
  5. Re-Add the Taskily project which should now be in src/Taskily/Taskily.xproj
  6. Compile

The error message was so not intuitive,and was bugging me for ages!

MVC Integration Test

Next, I needed to somehow integrate the startup configuration, which contains the JSON.Net serializer settings, etc. in my tests so I followed the Microsofts Integration Testing Article which allows you to create an in memory host of your MVC app with custom such as the following:

public class ApiTests
{
    private readonly HttpClient _client;

    public ApiTests()
    {
        var server = new TestServer(new WebHostBuilder()
                .UseStartup<Startup>()
        );

        _client = server.CreateClient();
    }

    [Fact]
    public async void Get_Returns2Values()
    {
        var response = await _client.GetAsync("https://localhost/api/Alexa/");

        var res = await response.Content.ReadAsStringAsync();

        res.Should().Be(@"[""value1"",""value2""]");
    }
}

In order for this to run, I had to add the following NuGet package dependency in order to be able to use the AspNetCore TestServer class.

"Microsoft.AspNetCore.TestHost": "1.1.0-preview1-final", 

UserSecrets

This allowed me to compile and run the test, but straight away the test failed as I was using UserSecrets, as specified in Microsoft’s Safe storage of app secrets during development article, and one of my Middleware was throwing an exception that the value cannot be null.

So I added a user secrets Id into the test project.json:

{
  ....
  "userSecretsId": "aspnet-WebApplication1-19b75b8b-b10e-4fea-94e8-17c9f955732e"
}

and also added the SecretManager tool into the project via project.json which allows me to manage the user secrets for the project via command line:

{
  .....
  "tools": {
    "Microsoft.Extensions.SecretManager.Tools": "1.0.0-preview2-final"
  },
  .....
}

I then set my 2 user secrets that one of my AspnetCore middleware’s needed by

  1. dotnet restore (Ctrl+Shift+K, Ctrl+Shift+R in VS on the project)
  2. Open Command Prompt in the test folder
  3. run the following command

    dotnet user-secrets set ClientID MySecretClientIdValue dotnet user-secrets set ClientIDPassword PasswordThatYouWillNeverGuess

Environment

Now that the UserSecrets were setup, my test was still failing with the same error (that the ClientID is null, which is retrieved from UserSecrets).

After a bit of digging around I noticed the following:

if (env.IsDevelopment())
{
    // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
    builder.AddUserSecrets();

    // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
    builder.AddApplicationInsightsSettings(developerMode: true);
}

which meant, that ASP.NET Core will only use UserSecrets when the server is running in Development environment. I could change this but it’s probably like that for a reason! So instead decided to set the environment variable on the TestServer by using the WebHostBuilder like the following:

var server = new TestServer(new WebHostBuilder()
        .UseEnvironment(EnvironmentName.Development) //<-- Setting Environment Variable
        .UseStartup<Startup>()
);

Now my test was running successfully and Bobs your uncle! 😀

Happy Coding