efcore: Exception when trying to use InMemoryDatabase for unit testing

I’m not sure whether this goes here or in the Testing repo but it’s relating to EF. I’m trying to get xUnit working with EF’s InMemoryDatabase but I’m having an issue I can’t get around and none of the documentation is helping. I’ve Google’d around for my error but no one else seems to have experiencing it. It’s either the documentation is out of date or I’m doing something wrong.

I have the following controller test constructor (it’s failing at the constructor so I guess no more is needed):

public ApiAuthControllerTest()
{
    serviceProvider = new ServiceCollection()
        .AddEntityFrameworkInMemoryDatabase()
        .BuildServiceProvider();

    var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseInMemoryDatabase()
        .UseInternalServiceProvider(serviceProvider);

    loggerFactory = new TestLoggerFactory();

    context = new ApplicationDbContext(optionsBuilder.Options);

    controller = new ApiAuthController(context, null, null, null, null, null, loggerFactory);
}

And when I run the test from the command line I’m getting the following exception:

System.InvalidOperationException : Unable to resolve service for type 'Microsoft.EntityFrameworkCore.Storage.IRelationalConnection'. This is often because no database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
  Stack Trace:
       at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
       at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.SetCommandTimeout(DatabaseFacade databaseFacade, Nullable`1 timeout)
       at Project.Models.ApplicationDbContext..ctor(DbContextOptions`1 options)
       at Project.Tests.Controllers.ApiAuthControllerTest..ctor()

I’m probably doing something wrong but I’ve tried every variation from the documentation and posts I’ve found around the web to no avail. A little guidance here would be extremely appreciated.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 26 (12 by maintainers)

Most upvoted comments

@ajcvickers Strange that you suggest SQLite in memory mode as a solution on every post and I have for example 30+ integration tests using in-memory database that works great. Now I have only 1-2 particular cases where I need to use raw SQL because Range Remove in Entity Framework is doing foreach and not work as expected and you suggest that I drop everything and migrate to SQLite.

Is there a option to supply raw SELECT for Range Remove and then use that select to form Raw delete that will be used instead of RangeRemove? Similar like we have with Adapter / Dataset in Visual Studio 2003 where we supplied Select and based on that Adapter generated automatically Insert/Update/Delete

This should be reopened. The problem is not the lack of support, the problem is it throws an exception. Let the InMemoryDb ignore the relational-db specific calls, they shouldn’t really be part of testable business logic anyway… but the way things are right now, you basically cannot use the in-memory db context for its intended purpose if you have any code that references an unsupported feature. That’s just bad design.

@Jargon64 When you are using the in-memory database you can’t make calls to relational-specific code. SetCommandTimeout is one such call. See #5150 for a discussion of testing relational code.

If you are inject DbContext in your method then InMemory provider also works because all the relational configuration should happen before DbContext is injected. But if you are setting CommandTimeOut inside your method then you cannot test that method. That method is not abstracted out with work with any DbContext provider. You are using relational code.

The only business logic here that’s really testable is, “if the foo with the supplied ID doesn’t exist, create it and return the result”.

Though you say about testing only business logic, you are still testing DbContext. If you want to just test busiless logic then you should have a method which fetches you foo, if null then create a new foo and call a method to add it to database. That way you can mock the methods which does actual DbContext tasks and test only your business logic.

Mocking DbContext is not recommended but if you are saying that you want to test “business logic” which does not require connecting to database, then refactor your code to unit test business logic. If connecting to DbContext is required then use InMemory DbContext but make sure your code is not using something which is provider specific like command timeout/transaction. If your code is using relational features than you have to use relational database.

If you don’t want to test relational features then don’t call into code which uses them. You may need to refactor to separate out that from your business logic.

@smitpatel

If something is not part of testable business logic then probably shouldn’t test it.

…that’s what I’m saying. If you inject a dbcontext as a service into your class, you shouldn’t be testing any of its functionality, you should just simulate what it would do under “normal” circumstances so you can test what your class does with the service. The db context is already extremely difficult/ impossible to mock as you would normally do with an injected service; the in-memory dbcontext is the generally encouraged way to “mock” the context. But… if it throws exceptions because some particular feature is meaningless (like a CommandTimeout… which is actually not a “relational db” feature at all, but rather an I/O issue, likewise transactions are a transactional feature and not a relational feature) then you cannot use this method to test what actually should be tested.

We shouldn’t be testing relational features, or literally any db context features in our classes, but that doesn’t mean we shouldn’t be using relational features. Those should be tested by the writers of the db context code. If I put a CommandTimeout of 120 for a particular method, I’m just circumventing a problem that has nothing to do with the code I’m writing, but rather DB system behind the abstraction of the db context. I’m not testing that. Likewise, for transactions, I shouldn’t be testing that some data operation is properly rolled back, that code is someone else’s, and should already be tested; I should be testing that I’m taking some action upon a rollback event, because that’s the business logic that I wrote.

For example, let’s say I have a service class like so:

public class FooService : IFooService
{
    private readonly MyDbContext _context;
    public FooService(MyDbContext context)
    {
        _context = context;
    }

    public async Task<Foo> GetTheFoo(ing fooId)
    {
        var foo = await _context.Foos.FirstOrDefaultAsync(x => x.Id == fooId);
        if (foo == null) 
        {
            foo = new Foo {Id = fooId, Status = Status.New};
            await _context.AddAsync(foo);
            await _context.SaveChangesAsync();
        }
        return foo;
    }
}

The only business logic here that’s really testable is, “if the foo with the supplied ID doesn’t exist, create it and return the result”.

This would be a perfect use case for using an in-memory context for testing… but if we have a SetCommandTimeout in the db context definition, we can’t do it. That really does not make a lick of sense to me.