4

I have a number of controllers that I am testing, each of which has a dependency on a repository. This is how I am supplying the mocked repository in the case of each test fixture:

[SetUp]
public void SetUp()
{
    var repository = RepositoryMockHelper.MockRepository();
    controller = new HomeController(repository.Object);
}

And here is the MockRepository helper method for good measure:

internal static Mock<IRepository> MockRepository()
{
    var repository = new Mock<IRepository>();

    var posts = new InMemoryDbSet<Post>()
    {
        new Post {
            ...
        },
        ...
    };

    repository.Setup(db => db.Posts).Returns(posts);

    return repository;
}
... = code removed for the sake of brevity. 

My intention is to use a new instance of InMemoryDbSet for each test. I thought that using the SetUp attribute would achieve this but clearly not.

When I run all of the tests, the results are inconsistent as the tests do not appear to be isolated. One test will for example, remove an element from from the data store and assert that the count has been decremented but according to the whim of the test runner, another test might have incremented the count, causing both tests to fail.

Am I approaching these tests in the correct way? How can I resolve this issue?

Caster Troy
  • 2,796
  • 2
  • 25
  • 45

3 Answers3

3

The package you reference you are using for your InMemoryDataSet uses a static backing data structure, and so will persist across test runs. This is why you're seeing the inconsistent behaviors. You can get around this by using another package (as you mention), or pass in a new HashSet to the constructor in every test so it doesn't use a static member.

As to the rest of your question, I think you're approaching the testing well. The only thing is that since all of your tests have the same setup (in your SetUp method), they could influence each other. I.e., if you have a test that relies on having no Foo objects in the set and one that needs at least one, then you're either going to add one in SetUp and remove it in the test that doesn't need it, or vice versa. It could be more clear to have specific set up procedures as @BillSambrone mentioned.

Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
  • 1
    It was @BillSambrone who helped *me* solve this problem originally, but I feel as though this answer will be the most useful for those experiencing similar problems and will therefore mark it as the accepted answer. – Caster Troy Jul 10 '14 at 22:07
2

As @PatrickQuirk pointed out, I think your problem is due to what InMemoryDbSet does under the covers.

Regarding the "Am I approaching this right ?" part :

If, as I suspect, your Repository exposes some kind of IDbSet, it's probably a leaky abstraction. The contract of an IDbSet is far too specific for what a typical Repository client wants to do with the data. Better to return an IEnumerable or some sort of read-only collection instead.

From what you describe, it seems that consumers of Posts will manipulate it as a read-write collection. This isn't a typical Repository implementation - you'd normally have separate methods such as Get(), Add(), etc. The actual internal data collection is never exposed, which means you can easily stub or mock out just the individual Repository operations you need and not fear the kind of issues you had with your test data.

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • You make some good points about the repository. Previously I used a conventional repository but in my experience it was a hindrance. Working against the `IDBSet` is so much easier here. – Caster Troy Jul 10 '14 at 22:05
1

Whatever is in your [SetUp] method will be called for each test. This is probably behavior that you don't want.

You can either place the code you have in the [SetUp] method inside each individual test, or you can create a separate private method within your unit test class that will spin up a freshly mocked DbSet for you to keep things DRY.

Bill Sambrone
  • 4,334
  • 4
  • 48
  • 70
  • I don't know man. I can see your suggestion working but it appears to me that the `SetUp` method should do the same thing automatically. According to the documentation the `SetUp` method is invoked *"just before each test method is called."* This sounds a lot like calling a private method manually before each test to me. – Caster Troy Jul 07 '14 at 19:00
  • Do your tests depend on each other, as in the tests need to be run in a certain order? If not, you want an isolated mocked repository for each test. Put a breakpoint in the setup method, and you will see that it gets hit for each test. – Bill Sambrone Jul 07 '14 at 19:04
  • They do not depend on each other by design. An isolated mock repository for each test *is* what I want. – Caster Troy Jul 07 '14 at 19:06
  • For whatever reason, that inmemorydbset is persisting between tests. Looking into an answer on the "why" behind that, and will update my answer once I find something useful. – Bill Sambrone Jul 07 '14 at 19:13
  • Ok Bill. I will make that my focus. [Here](https://www.nuget.org/packages/FakeDbSet/) is the package I am using. You might be interested to know that I have verified that the `MockRepository` helper method is being called an appropriate number of times. – Caster Troy Jul 07 '14 at 19:22
  • Ooo. I replaced the package mentioned in my previous comment with [this class](http://stackoverflow.com/a/5022512/1500199) and now all is well. – Caster Troy Jul 07 '14 at 19:28
  • Glad it is working! I'm still going to look into that original package for my own info, as I'm really curious as to why it is persisting data even after new'ing up a new mock. – Bill Sambrone Jul 07 '14 at 19:30
  • Me too bud. I am doing the same thing. Let me know if you find anything. I will answer my own question later unless you beat me to it. – Caster Troy Jul 07 '14 at 19:30