rgvlee/EntityFrameworkCore.Testing

Can I use dependancy injection with this lib?

Closed this issue · 3 comments

I'm trying to implement a test harness in order to run integration tests on my application. I can't seem to find much about injecting the mocked context in place of the standard context and when I do attempt to do this with:

IServiceProvider serviceProvider = new ServiceCollection()
                                        .AddEntityFrameworkInMemoryDatabase()
                                        .BuildServiceProvider();

var loggerMock = Mock.Of<ILogger<DataContext>>();
var contextOptions = new DbContextOptionsBuilder<DataContext>().UseInMemoryDatabase("xx")
                                                                .UseInternalServiceProvider(serviceProvider)
                                                                .Options;
var contextMock = Create.MockedDbContextFor<DataContext>(loggerMock, contextOptions);

services.AddDbContext<DataContext>(options =>
{
    options.UseInMemoryDatabase("xx");
    options.UseInternalServiceProvider(serviceProvider);
    options.ConfigureWarnings(
        warningsConfigurationBuilder
            => warningsConfigurationBuilder.Ignore(InMemoryEventId.TransactionIgnoredWarning));
},
    ServiceLifetime.Singleton);

I just get the following error:

Message: Unable to create instance of class ****.ClientLibrary.Test.ClientLibraryTest. Error: System.MissingMethodException: Constructor on type '****.Repository.DataContext' not found..

Forgot to include version, am currently utilising v1.2.1

To answer the question directly, yes you could use the mocked db context with the MS DI container. But first, what is the use case/specific reason for using a service provider/DI container in your tests?

Typically you wouldn't use a service provider/DI container in a unit test. You want to test a single concern and to achieve this you generally end up mocking your dependencies; a service provider/DI container is a bit of a third wheel in that relationship. If you were doing integration tests you normally wouldn't mock your dependencies, db context included.

The following is a contrived example of what I'd consider contemporary usage in a unit test:

public interface IFoo
{
  void DidSomething();
}

public class Bar
{
    ILogger<Bar> _logger;
    DataContext _dbContext;
    IFoo _foo;

    public Bar(ILogger<Bar> logger, DataContext dbContext, IFoo foo)
    {
      _logger = logger;
      _dbContext = dbContext;
      _foo = foo;
    }

    public void DoSomething()
    {
      _foo.DidSomething();

      //other operations...
    }
}

I'd expect the unit test to something like

var mockedDbContext = Create.MockedDbContextFor<DataContext>(
    Mock.Of<ILogger<DataContext>>(), 
    new DbContextOptionsBuilder<DataContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options
);
var mockedFoo = Mock.Of<IFoo>();

var service = new Bar(Mock.Of<ILogger<Bar>>(), mockedDbContext, mockedFoo);

service.DoSomething();

Mock.Get(mockedFoo).Verify(x => x.DidSomething(), Times.Once);

//other more useful asserts...

If there is a genuine reason for using a service provider then you simply need to add the mocked db context like any other service:

var mockedDbContext = Create.MockedDbContextFor<DataContext>(
    Mock.Of<ILogger<DataContext>>(),
    new DbContextOptionsBuilder<DataContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options
);

services.AddSingleton<DataContext>(mockedDbContext);

Issue closed for now; happy to continue if further assistance is required.