Integration Testing with ASP.NET Core

Setting up integration testing is one of those things that you do once per project and then promptly forget. I usually have to look it up again each time, so instead I’m documenting it here, so that it’s easy to find in the future.

In this context, the integration tests will hit a real endpoint (using TestServer) and run all of the code in the API to serve the response, but mocking any external dependencies to the service (DB, Auth, etc.). I’ll walk through mocking out auth and custom services in a future article.

I’m going to skip to the end and do that first. Here’s how to setup the Test Project:

  1. Create a class library project
  2. Add the following references:
    • EntityFrameworkCoreMock.Moq
    • Microsoft.AspNetCore.TestHost
    • Microsoft.NET.Test.Sdk
    • Moq
    • xunit.assert
    • xunit.core
    • xunit.runner.visualstudio
    • TheProject
  3. Create a TestStartup file at the root with the following:
public class TestStartup {
    private DbContextMock<ProductContext> DbContextMock;

    public void ConfigureServices(IServiceCollection services) {
        services.AddAutoMapper(typeof(Startup));
        services.AddMediatR(typeof(Startup));

        services
            .AddHttpContextAccessor()
            .AddHttpClient();
        services.AddMvc()
            .AddApplicationPart(typeof(Startup).Assembly);

        this.DbContextMock = new DbContextMock<ProductContext>();
        services.AddSingleton(DbContextMock);
        services.AddSingleton(DbContextMock.Object);
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
        app.UseRouting();
        app.UseEndpoints(endpoints => {
            endpoints.MapControllers();
        });
    }
}

That should be all of the hard work done. A test looks like this now:

[Fact]
public async Task QueryByValidId_Returns200WithResult() {
    var id = Guid.NewGuid();
    var value = 0;

    using (var server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>()))
    using (var client = server.CreateClient())
    using (var scope = server.Services.CreateScope())
    {
        scope.ServiceProvider
            .GetRequiredService<DbContextMock<ProductContext>>()
            .CreateDbSetMock(
                db => db.DataPoints,
                new[] { new DataPoint() { Id = id, Value = value } });

        var request = new HttpRequestMessage(HttpMethod.Get, $"/api/datapoints/{id}");
        var response = await client.SendAsync(request);

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        var responseBody = await response.Content.ReadAsStringAsync();
        var responseObject = JsonConvert.DeserializeObject<DataPointDto>(responseBody);

        Assert.Equal(id, responseObject.Id);
        Assert.Equal(value, responseObject.Value);
    }
}

Protips:

  • Deserialize the response after asserting the status code… that way if your data contract isn’t correct, you’ll get the response code asserted before it throws serialization exception.
  • You can set the return value of CreateDbSetMock to be a variable and that will allow you to assert the contents of the “database” after the endpoint executes.

Code is here.

Hope it helps.

What are your thoughts?

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.