2

When reading/writing to a context from Entity Framework I often read, that it is recommended to keep the context's lifecycle as short as possible (one context per unit of work). It makes a lot of sense to do that, however how am I supposed to write a unit test for a class that is creating and disposing this context in every method?

Let's see a simplified fictional example code snippet:

public class Resource : IResource {
    public Item GetItem(string name) {
        using(var context = new DbContext()) {
            return context.Items.FirstOrDefault(item => item.Name == name);
        }
    }
}

If I want to unit-test the Resource, I would like to mock the DbContext, so it returns some fake data for me.

My usual approach would be to make the DbContext a property of my class and inject it from outside like this:

public class Resource : IResource {
    public DbContext Context { private get; set; }

    public Item GetItem(string name) {
        return this.Context.Items.FirstOrDefault(item => item.Name == name);
    }
}

This way, the instanciating class which injects the context is responsible for the life-cycle and I can omit the using and I can inject a mocked context of course.

Now taking into consideration, that providing a long living context might be a bad idea and following the one context per unit of work principle, this option is not favourable.

So what options do I have? One idea is to inject a ContextFactory, which creates and disposes the context on demand like this:

public class Resource : IResource {
    public DbContextFactory ContextFactory { private get; set; }

    public Item GetItem(string name) {
        using(var context = ContextFactory.CreateContext()) {
            return context.Items.FirstOrDefault(item => item.Name == name);
        }
    }
}

Does this make sense or am I going into a completely wrong directon?

Jan
  • 2,646
  • 24
  • 34
  • I believe what you are trying to do is an integration test. For a unit test the `IRepository` that uses the `DbContext` should be Mocked. – Dustin Kingen Mar 08 '13 at 17:10
  • I'm not sure, what this has to do with the actual problem, but it is a unit-test. I want to test the Repository implementation. My unit-test class is called `RepositoryTests` and one of the tests would be like `GetItemThrowsIfNotFound` or `GetItemReturnsItem` and so on. This is clearly a unit-test. And in order to test the repository implementation, I want to mock a dbContext, because this is tested elsewhere. – Jan Mar 09 '13 at 09:59
  • See [What's the difference between unit tests and integration tests?](http://stackoverflow.com/questions/5357601/whats-the-difference-between-unit-tests-and-integration-tests) – Dustin Kingen Mar 09 '13 at 17:56
  • What does your implementation of ContextFactory look like? I am running into the same issue you are having now. And I want to be able to mock out the context just like you. How do you mock out the context factory? do you make an abstract class of it? and set it in the unit test? – Mitch May 20 '16 at 14:21

2 Answers2

1

The option is to use Method Injection:

   public Item GetItem(string name, DbContext context) {
        using(var context) {
            return context.Items.FirstOrDefault(item => item.Id == id);
        }
    }

Also Seemann in his book Dependency Injection in .Net uses this method to inject dependency which can change from call to call.

But I'd use ContextFactory.

Anton Sizikov
  • 8,794
  • 1
  • 28
  • 39
  • Oh right! I should have written this in my question. I had this idea too, but abandoned it, because there might be many methods in my class which all use the same context. To inject the same context in every method would be over-kill, but thanks for mentioning this option! – Jan Mar 08 '13 at 16:54
  • 3
    If you aim on reusing the context between calls you should remove the using statement from this method. – bryanbcook Mar 09 '13 at 14:23
  • 1
    I think the using should be removed in any case. If the context is provided as a method argument, the method should not interfere with the context's life-cycle. Only the one who instanciated the context should be responsible for its life-cycle. – Jan Mar 09 '13 at 20:55
  • Yep, I agree with Jan, the method which disposes context is not "pure", this behaviour has non-obviouse side effects. – Anton Sizikov Mar 10 '13 at 08:57
1

I checked out Mark Seemann's book (mentioned by Anton Sizikov) and he actually uses a factory as well, calling it an ephemeral disposable. It is somehow a Leaky Abstraction, because the factory manages the disposable dependency's life time instead of the DI container. Still it's better than having a memory leak due to undisposed disposables or exceptions caused by disposed shared dependencies.

Jan
  • 2,646
  • 24
  • 34