A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://stackoverflow.com/questions/79694284/ef-core-in-memory-unit-test-fails below:

xunit - EF Core in-memory unit test fails

The in-memory provider, tempting as it is, isn't really a good test-double (and Microsoft says as much in the EF documentation). Assuming you're using it (as I originally attempted) in order to have a database that was consistent across test runs, then it is possible to achieve something similar with a "live" database.

To begin, I'll assume that you've either set up a testing database with arbitrary data, or you've made a copy of "production" data and created a testing database from that data.

The key is to wrap every single EF call in a transaction. Then, at the end of the test, clear the change tracker and rollback any changes to the database. The below is derived from one of my unit tests:

using Microsoft.Extensions.Configuration;

using Xunit;

using System;
using System.Linq;

namespace YourNamespace.Tests
{
    public class TestCase : IDisposable
    {
        private IConfiguration configuration;

        private YourDbContext context;

        public TestCase()
        {
            configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();
                
            // Get a new, clean context for each unit test
            context = DbContextFactory.GetNewTestContext(configuration.GetConnectionString("name-of-connection"));

            // Make sure every database call is made within a transaction
            context.Database.BeginTransaction();
        }

        public void Dispose()
        {
            // Remove any entities with changes currently being tracked
            context.ChangeTracker.Clear();

            // Rollback any inserts or updates
            context.Database.RollbackTransaction();

            context.Dispose();
        }

    }
    

    public class DbContextFactory
    {
        public static YourDbContext GetNewTestContext(string connectionString)
        {
            var options = new DbContextOptionsBuilder<YourDbContext>()
                .UseSqlServer(connectionString)
                .EnableDetailedErrors(true)
                .EnableSensitiveDataLogging(true)
                .Options;

            var context = new YourDbContext(options); 

            return context;
        }
    }
}

I use NUnit, where there are setup and teardown methods, but if I'm reading the XUnit docs correctly, this should work the same way.

Basically, in the test's constructor, instantiate the DbContext and enroll everything that follows into a transaction. Then, in the Dispose() method, you discard changes and roll everything back.

I don't like repeating code, so I create something like the DbContextFactory shown here to generate my database contexts. It's not required, though.

There is a (somewhat) major caveat to this approach: auto-incrementing IDs do not get reset with the transaction rollback, so you'll see those ID values increment across tests. Just something to be aware of.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4