title: ASP.NET MVC CORE web application integration tests with EF Core in-memory database - fresh database for each test
body: <p>I am learning about ASP.NET Core 3 and have built a basic application. I am looking run integration tests to assert calls to the controllers read/write from the database correctly. To avoid having to rely on the actual database I am looking at using EF Core's in-memory database. I have been following <a href="https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1" rel="nofollow noreferrer">this</a> article as my main guide.</p>
  
  <p>The problem I have is that I am struggling to ensure each separate integration test uses a fresh database context.</p>
  
  <p>Initially, I encountered errors calling my database seed method more than once (the second and subsequent calls failed to add a duplicate primary key - essentially it was using the same context).</p>
  
  <p>From looking at various blogs, tutorial and other questions here, I worked around this by instantiating the in-memory database with a unique name (using <code>Guid.NewGuid()</code>). This <em>should</em> have solved my problem. However, this gave me a different issue. The database seed method was correctly called at each test initialisation, however when I then called a controller action the dependency injection instantiated a <em>new</em> database context, meaning that no seed data was present!</p>
  
  <p>I seem to be going in circles either only being able to call seed data once, and only being able to have a single test, or having more than one test but with no seed data!</p>
  
  <p>I have experimented with the scope lifetimes for the DbContext service, setting this to transient/scoped/singleton, but with seemingly no difference in results.</p>
  
  <p>The only way I have managed to get this to work is to add a call to <code>db.Database.EnsureDeleted()</code> <em>before</em> the call to <code>db.Database.EnsureCreated()</code> in the seed method, but this seems like a massive hack and doesn't feel right.</p>
  
  <p>Posted below is my utilities class to set up the in-memory database for the tests, and a test class. Hopefully this is sufficient, as I feel this post is long enough as it is, but the actual controller / startup class can be posted if necessary (though they are fairly vanilla).</p>
  
  <p>Any help much appreciated.</p>
  
  <p><strong>Utilities class to set up the in-memory database</strong></p>
  
  <pre class="lang-cs prettyprint-override"><code>using CompetitionStats.Entities;
  using Microsoft.AspNetCore.Hosting;
  using Microsoft.AspNetCore.Mvc.Testing;
  using Microsoft.EntityFrameworkCore;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Logging;
  using System;
  using System.Linq;
  
  namespace CompetitionStatsUnitTests
  {
      class Utilities
      {
          internal class CustomWebApplicationFactory&lt;TStartup&gt;
              : WebApplicationFactory&lt;TStartup&gt; where TStartup : class
          {
              protected override void ConfigureWebHost(IWebHostBuilder builder)
              {
                  builder.ConfigureServices(services =&gt;
                  {
                      // Remove the app's ApplicationDbContext registration.
                      var descriptor = services.SingleOrDefault(
                          d =&gt; d.ServiceType == typeof(DbContextOptions&lt;CompetitionStatsContext&gt;));
  
                      if (descriptor != null)
                      {
                          services.Remove(descriptor);
                      }
  
                      // Add ApplicationDbContext using an in-memory database for testing.
                      services.AddDbContext&lt;CompetitionStatsContext&gt;(options =&gt;
                      {
                          options.UseInMemoryDatabase("InMemoryDbForTesting");
                      });
  
                      // Build the service provider.
                      var sp = services.BuildServiceProvider();
  
                      // Create a scope to obtain a reference to the database context (ApplicationDbContext).
                      using (var scope = sp.CreateScope())
                      {
                          var scopedServices = scope.ServiceProvider;
                          var db = scopedServices.GetRequiredService&lt;CompetitionStatsContext&gt;();
                          var logger = scopedServices.GetRequiredService&lt;ILogger&lt;CustomWebApplicationFactory&lt;TStartup&gt;&gt;&gt;();
                          db.Database.EnsureDeleted();  // feels hacky - don't think this is good practice, but does achieve my intention
                          db.Database.EnsureCreated();
  
                          try
                          {
                              InitializeDbForTests(db);
                          }
                          catch (Exception ex)
                          {
                              logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {Message}}", ex.Message);
                          }
                      }
                  });
              }
  
              private static void InitializeDbForTests(CompetitionStatsContext db)
              {
                  db.Teams.Add(new CompetitionStats.Models.TeamDTO
                  {
                      Id = new Guid("3b477978-f280-11e9-8490-a8667f2f93c4"),
                      Name = "Arsenal"
                  });
  
                  db.SaveChanges();
              }
          }
      }
  }
  </code></pre>
  
  <p><strong>Test class</strong></p>
  
  <pre class="lang-cs prettyprint-override"><code>using Microsoft.VisualStudio.TestTools.UnitTesting;
  using System.Net.Http;
  using System.Threading.Tasks;
  
  namespace CompetitionStatsUnitTests.ControllerUnitTests
  {
      [TestClass]
      public class TeamControllerTest
      {
          private HttpClient _testClient;
  
          [TestInitialize]
          public void Initialize()
          {
              var factory = new Utilities.CustomWebApplicationFactory&lt;CompetitionStats.Startup&gt;();
              this._testClient = factory.CreateClient();
          }
  
          [TestMethod]
          public async Task TeamController_GetTeam_Returns_Team()
          {
              var actualResponse = await this._testClient.GetStringAsync("api/teams/3b477978-f280-11e9-8490-a8667f2f93c4");
              var expectedResponse = @"{""id"":""3b477978-f280-11e9-8490-a8667f2f93c4"",""name"":""Arsenal""}";
              Assert.AreEqual(expectedResponse, actualResponse);
          }
  
          [TestMethod]
          public async Task TeamController_PostTeam_Adds_Team()
          {
              var content = new StringContent(@"{""Name"": ""Liverpool FC""}", System.Text.Encoding.UTF8, "application/json");
              var response = await this._testClient.PostAsync("api/teams/", content);
              Assert.AreEqual(response.StatusCode, System.Net.HttpStatusCode.Created);
          }
      }
  }
  </code></pre>