Nathan
Nathan

Reputation: 61

How to replace IDbContextFactory in Service Injection for xUnit integration test

I am setting up an API using .NET 5 with dependency injection for my data access; using Entity Framework code first. I need to use IDbContextFactory because some of my controllers need to use more than one DbContext instance for proper Unit of Work scope.

I'm also trying to use xUnit for integration tests and am running into difficulties getting it to use in memory database.

Here is the relevant snippet from my Startup class

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContextFactory<ApplicationDbContext>(
                options => options.UseSqlServer("connection string")
            );

            //other services configured
        }

And this is the xUnit Application Factory that is supposed to remove the existing DbContextFactory and replace it with the InMemory factory.

public class TestApplicationFactory<TStartup>
        : WebApplicationFactory<TStartup> where TStartup: class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                var contextFactory = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(IDbContextFactory<ApplicationDbContext>)
                );
                services.Remove(contextFactory);
                
                services.AddDbContextFactory<ApplicationDbContext>(
                    options => options.UseInMemoryDatabase("InMemoryDbForTesting")
                );

                var sp = services.BuildServiceProvider();
                var dbf = sp.GetRequiredService<IDbContextFactory<ApplicationDbContext>>();
                var db = dbf.CreateDbContext();
                db.Database.EnsureCreated();

                // Pass the context to a class that will add seed data
                DbSeed.InitializeDbForTests(db);
            });
        }

When I step through in debug, it appears to be removing the existing factory as evidenced by the count of services dropping after the services.Remove(contextFactory); line and it also seems to add the new one, again by looking at the count.

This is not throwing any errors, it's just not using the new DbContextFactory.

What am I missing here?

Thank you in advance for any help.

Upvotes: 2

Views: 2216

Answers (2)

Cerchez Calin
Cerchez Calin

Reputation: 41

You could just replace the DbContextOptions<ApplicationDbContext>

Upvotes: 1

Nathan
Nathan

Reputation: 61

I figured out the issue.

When the Startup class runs

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContextFactory<ApplicationDbContext>(
                options => options.UseSqlServer("connection string")
            );

            //other services configured
        }

It is actually adding four descriptors to the service collection:

  1. IDbContextFactory\<ApplicationDbContext>
    
  2. IDbContextFactorySource\<ApplicationDbContext>
    
  3. DbContextOptions
    
  4. DbContextOptions\<ApplicationDbContext>
    

The solution is to remove all of these in the xUnit ApplicationFactory. I was only removing one. Once I removed the other three, I could continue on to add the InMemory version of the DbContextFactory and it now correctly points to the test database.

If you have a more efficient or elegant solution, I'd be interested to know.

Upvotes: 4

Related Questions