5

I am currently working on an ASP.NET Core 2.0 Web API with EF Core 2.0. I plan to implement the Repository + UnitOfWork pattern and I've already created the abstractions, interfaces etc. Since I'm following a TDD approach I want to write tests on those interfaces before proceeding with the implementation.

The issue I'm facing is mostly related to semantics. I have created two projects in my solution, one for Unit Tests and one for Integration Tests. It's obvious to me that a test that also tests the database is not a unit test and should therefore be placed in the IntegrationTests project. The thing is though, that I am planning on using the EntityFrameworkCore.InMemory provider and boot up a fake in memory database for each test.

So every test should have this structure:

[TestClass]
public class GamesRepositoryTest
{
    AppDbContext _context;
    IGamesRepository _repository;

    public GamesRepositoryTest()
    {

    }

    [TestInitialize]
    public void Initialize()
    {
        DbContextOptionsBuilder<AppDbContext> builder = new DbContextOptionsBuilder<AppDbContext>().UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString());
        _context = new AppDbContext(builder.Options);
        _repository = new GamesRepository(_context);
    }

    [TestCleanup]
    public void Cleanup()
    {
        _context.Database.EnsureDeleted();
    }
}

So my question is, does EntityFrameworkCore.InMemory provide an adequate level of abstraction so that the above class can be considered a unit test or should I place it in the IntegrationTests project?

8
  • 2
    EFCore InMemory database isn't a relational database, as such it doesn't support relational database features such as enforcing of constraints, such as foreign-key relationships etc. It's meant just for integration tests. If you need something "closer" to a relational database you should instead use SqLite provider with inmemory sqlite database. See the docs for more Commented Nov 1, 2017 at 13:51
  • @Tseng I don't want to test the constraints. I'm exclusively interested in testing the queries that happen in the repositories and I'm just searching for a quick replacement of the persistence layer. My question could be described as "Is every test that involves EntityFramework an integration test?" Commented Nov 1, 2017 at 13:55
  • 1
    Yes, it is. Since EF Core can't be mocked, the only way to do test involving it, is to inject it in the class you test (repository, service, command handler, etc.) Commented Nov 1, 2017 at 13:57
  • 1
    @dimlucas your repository implementation also seems to be tightly coupled to implementation concerns. If it was dependent on an abstraction then this talk about in-memory this and that would be a non factor if the goal is to do an actual unit test. Commented Nov 1, 2017 at 14:01
  • 1
    @dimlucas no you misunderstand. If your repositories are loosely coupled you can unit test them in isolation with specifically mocked dependencies. Commented Nov 1, 2017 at 14:16

2 Answers 2

22

The new in-memory provider has confused the stuffing out of everyone. First and foremost let's just get its purpose cleared up: it's a test data provider. That's it. You could just as easily mock out the DbContext and return a static list or something. The in-memory provider just gives you an easier way to set up that test scaffold. It's not appropriate for integration testing, because you wouldn't actually use it in production. A proper integration test would hit a real facsimile of your production setup.

That said, the chief problem with the new in-memory provider for Entity Framework Core is that now people seem to be abusing it to test things that they shouldn't be testing. EF Core is already well-tested, so you should only be testing your application code. Again, just think of it like a mock without actually having to setup a mock. As long as you do that, you should be fine.

Based on all that, to answer your question, your tests should be unit tests, if for no other reason than if you're actually creating an integration test, you shouldn't be using the in-memory provider. Just make sure you're actually doing unit tests.

Sign up to request clarification or add additional context in comments.

1 Comment

I second this train of thought.
4

If you're testing with a real dbContext, whether it's hitting an RDBMS or an InMemory provider, it's an integration test. Why? Because you're testing that framework's implementation, not your code running in your method. Unit tests just test the "unit" of code they're running, typically a single method (and not its collaborators).

EF Core's InMemory provider is not 100% the same as testing with a real database. In fact, there are quite a few differences. It's mostly useful IME at development time to show how the system works with some seeded data and for functional tests that can verify that the whole system is working as expected (for example, using TestServer).

If your unit test project is meant to only contain unit tests, then it shouldn't have any dependencies on infrastructure-specific code like EF Core. It should typically only have a dependency on your own project(s) that are being tested, and whatever tools you're using for your tests (e.g. xUnit, Moq, etc.). If you find that you're pulling in infrastructure dependencies, odds are you're writing integration tests.

3 Comments

It's partially wrong. Unit Tests can use InMemory Provider while you are testing the behavour of a method or a class and your implementation do not include specific sql instructions or transaction. Another alternative to InMemory provider is sqllite.
In that case you're still not just testing your code, you're testing your code and EF Core and how they work together. Unit tests should only depend on your code - other dependencies should be controlled. Updates to how EF Core works could break a test that uses your approach. Such updates wouldn't affect a unit test (using my definition). And in fact there are weird behaviors when using EF Core InMemory. For instance you can add an entity to EF Core for tracking and have it magically appear in a navigation property of another entity when you never explicitly added it there.
I advise to watch youtube video "Simplified Unit Testing with the Entity Framework Core InMemory Provider | Jason Taylor [LATEST]". I have no space and language knowledge to provide enough arguments to describe edge between unit and integrational tests

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.