How to mock a generic repository

How to mock a generic repository

June 27, 2012 2:17 pm

The short answer is: you don’t. You see, having a mocking library at hand (no matter how cool it is) doesn’t automatically make it the best tool for every testing need.

A generic repository is much easier to replace for testing with a simple fake and allows to use simple state-based testing agaist it, rather than mock verifications.

A fairly typical generic repository might look like the following:

public interface IRepository<T> where T : IEntity
{
    T Get(Guid id);
    void SaveOrUpdate(T entity);
    void Delete(T entity);
    IQueryable<T> Query();
}

You might use integer or long for IDs, you might not have an IEntity interface but a base class, you might not have a Query feature there, but that’s beside the point. The point is that such an interface, whatever the variations, is trivial to fake:

public class FakeRepository<T> : IRepository<T>
     where T : IEntity
{
    private Dictionary<Guid, T> data = new Dictionary<Guid, T>();

    public T Get(Guid id)
    {
        var entity = default(T);
        data.TryGetValue(id, out entity);
        return entity;
    }

    public void SaveOrUpdate(T entity)
    {
        data[entity.Id] = entity;
    }

    public void Delete(T entity)
    {
        data.Remove(entity.Id);
    }

    public IQueryable<T> Query()
    {
        return data.Values.AsQueryable();
    }
}

DONE. So why go for a mock, multiple setups, etc.? No need at all. This is a simple reusable fake that you can leverage over and over again in all tests.

What if you have more of a domain context-like repository that is more like a unit of work + entity collections? Something like:

public interface IDomainContext
{
    void Commit();

    T Find<T>(Guid id) where T : IEntity;

    void Save<T>(T entity) where T : IEntity;

    void Delete<T>(Guid id) where T : IEntity;

    IQueryable<Product> Products { get; }
    // Other entities
}

That’s also trivial to fake:

public class FakeDomainContext : IDomainContext
{
    private List<object> data = new List<object>();

    public void Commit()
    {
        throw new NotImplementedException();
    }

    public T Find<T>(Guid id) where T : IEntity
    {
        return data.OfType<T>().FirstOrDefault(x => x.Id == id);
    }

    public void Save<T>(T entity) where T : IEntity
    {
        var existing = data.OfType<T>().FirstOrDefault(x => x.Id == entity.Id);
        if (existing != null)
            data.Remove(existing);

        data.Add(entity);
    }

    public void Delete<T>(Guid id) where T : IEntity
    {
        data.RemoveAll(x => x is T && ((T)x).Id == id);
    }

    public IQueryable<Product> Products { get { return data.OfType<Product>().AsQueryable(); } }
}

Any other variations of the repository pattern or similar persistence abstraction you have, can simply be replaced with an in-memory one based on lists or dictionaries. Just don’t try to mock something that simple to fake.

/kzu

/kzu dev↻d