views:

99

answers:

3

I was wondering what I should test and how I should test an IRepository.

At the moment I created a MemoryRepository in my domain and I am testing that with fake data. I am not sure if this is correct though. Feels a little weird to first create some data and then test to see if the repository returns it correctly.

This is what my MemoryRepository looks like:

public class MemoryRepositoryUser : IRepositoryUser
{
    List<User> fakeUsers;       

    public MemoryRepositoryUser(List<User> fakeUsers)
    {
        this.fakeUsers = fakeUsers;
    }

    public IEnumerable<User> GetAll()
    {
        return fakeUsers;
    }

    public User GetById(string id)
    {
        return GetAll().Where(u => u.Username == id).Single();
    }
}

These are some tests I wrote:

[TestFixture]
class TestMemoryRepositoryUser
{
    private MemoryRepositoryUser repository;

    public TestMemoryRepositoryUser(){
        repository = new MemoryRepositoryUser(FakeData.GetFakeUsers());
    }

    [Test]
    public void Get_All_Users()
    {
        var Users = repository.GetAll();
        Assert.IsNotNull(Users);
        Assert.IsInstanceOf(typeof(IEnumerable<User>), Users);
        Assert.AreEqual(3, Users.Count());
    }

    [Test]
    public void Get_User_By_ID()
    {
        var Username = "Bob";
        var User = repository.GetById(Username);
        Assert.IsNotNull(User);
        Assert.AreEqual(Username, User.Username);
    }
}

I am pretty new to testing code and I mostly have problems with what I should test. I guess testing a MemoryRepository helps me to create all the features I need in the interface without having to talk to a database?

+4  A: 

Typically repository tests are integration tests and they do talk to the database. As you've noticed, there's little point to testing a repository with fake data.

It's usually the repository that's mocked to test other classes. By mocking every dependency except the class under test, you can isolate the functions that you are testing. The benefit of declaring interfaces for repositories is that it allows them to be easily mocked and used in unit tests of other code.

Jamie Ide
Thanks a lot for your answer.
Pickels
+1  A: 

One thing I notice, which may not be related to your question, is the state of repository should be independent of all the other tests that run. In this case, the same instance is acted upon by each test.

Consider what happens when you write a Delete_user test. It shouldn't matter if that test runs before Get_All_Users. I would new up a new MemoryRepositoryUser in each test. This test smell can exist in other tests and isn't specific to if you should test the fake or not.

Typically I would use a sub/mock in favor of a fake. As Jamie mentioned, the interface allows you to easily mock your dependencies.

Mark
Thanks that's a really good suggestion.
Pickels
A: 

If you plan to use your MemoryRepositoryUser only as a stub to test the behavior in the repository's client, then I recommend you don't test MemoryRepositoryUser itself for now. Instead, focus your energy on testing or test-driving the production implementation of IRepositoryUser.

On the other hand, remember that your production implementation of IRepositoryUser, which I'll call AdoBasedRepositoryUser (for the example where you implement to use ADO), needs to behave the same way as your stub. If not, then the tests for the repository's client might assume the wrong behavior in IRepositoryUser. You might consider some tests for that.

For example, when you test AdoBasedRepositoryUser, you will check GetById() by writing a test similar to this:

in AdoBasedRepositoryUserTest...
[Test]
GetById_RecordFound() {
    insert record with ID 762 directly into the USERS table
    User expected = User with ID 762 and the mandatory properties set
    User actual = new AdoBasedRepositoryUser().GetById(762);
    Assert.AreEqual(expected, actual);    // Implement User.Equals() to compare the values
}

You will want to verify that MemoryRepositoryUser also passes this test, so that you can use that to stub IRepositoryUser in tests for its client.

in MemoryRepositoryUserTest...
[Test]
GetById_RecordFound() {
    MemoryRepositoryUser repository = new MemoryRepositoryUser();
    User expected = User with ID 762 and the mandatory properties set
    repository.Add(expected);
    User actual = repository.GetById(762);
    Assert.AreEquals(expected, actual);
}

As long as you have tests in MemoryRepositoryUserTest that match the tests in AdoBasedRepositoryUser, then you know that your stub matches the production behavior. This way, you can safely use the stub when testing the Services that use this Repository.

After you've done this a few times, you might be ready to look at Contract Tests. (Google it.)

One last thing: I would name your repository interface IUserRepository instead of IRepositoryUser.

J. B. Rainsberger