Creating a Generic Base Fake Repository Class

In many enterprise level transactional applications, it is common to make use of a either a two or a three tier architecture, with one of the layers a data access layer. It is useful to have the data access layer loosely coupled, preferably via an IoC tool, in order to be able to “swap out” the real components with fake or stub versions.

For unit testing purposes this can be done using a mocking framework such as Rhino Mocks. It can also be useful to include hard-coded fake repository objects, not only for unit testing but also for prototyping either the front end or the service layer of the application, without requiring a real database and data access layer.

The IRepository Interface

In one of the applications I have been working on, my data access layer objects, implement (at a minimum), the following interface:

public interface IRepository<T> where T: class
    {
        T Get(int id);
        IList<T> GetAll();
        IList<T> GetBy(string find, string property, bool exactMatch);
        T Add(T item);
        T Update(T item);
        T Save(T item);
        IList<T> Save(IList<T> items);
        bool Delete(int id);
        bool DeleteAll();
    }

Creating Fake Repositories

Inspired partly by Gabriel Schenker’s article on the repository pattern, I created a set of fake repository objects to represent a set of hard-coded, fixed data along with dummy “working” operations. I soon realized that it would be beneficial to make use of a base class for my fake repositories, which provides generic implementations of all the required base methods, i.e.:

public class BaseFakeRepository<T> : IRepository<T> where T :class
{
protected Dictionary<int, T> _dictionary;
protected Expression<Func<T, int>> _identityExpression;

public BaseFakeRepository(Expression<Func<T, int>> identityExpression,
Dictionary<int, T> dictionary)
{
_identityExpression = identityExpression;
_dictionary = dictionary;
}

public BaseFakeRepository(Expression<Func<T, int>> identityExpression)
{
_identityExpression = identityExpression;
_dictionary = new Dictionary<int, T>();
}

public virtual T Get(int id)
{
return !_dictionary.ContainsKey(id) ? null : _dictionary[id];
}

public virtual IList<T> GetAll()
{
return _dictionary.Values.ToList();
}

public virtual IList<T> GetBy(string find, string property, bool exactMatch)
{
return _dictionary.Values.ToList().FindAll(x=>(exactMatch && x.GetType()
.GetProperty(property)
.GetValue(x, null).ToString().Equals(find)
|| (!exactMatch && (x.GetType().GetProperty(property).GetValue(x, null))
.ToString().Contains(find))));
}

public virtual T Add(T item)
{
var newKey = _dictionary.Count == 0 ? 1 : _dictionary.Keys.Max() + 1;
SetIdentityValue(item, newKey);
_dictionary.Add(newKey, item);
return item;
}

public virtual T Update(T item)
{
var identityValue = GetIdentityValue(item);

if (!_dictionary.ContainsKey(identityValue))
{
throw new Exception(“Cannot update”);

             }
            _dictionary[identityValue] = item;
            return item;
        }

        public virtual T Save(T item)
        {
            var identityValue = GetIdentityValue(item);
            return identityValue == 0 || !_dictionary.Keys.Contains(identityValue)
                ? Add(item) : Update(item);
        }

        public virtual IList<T> Save(IList<T> items)
        {
            var savedItems = new List<T>();
            items.ToList().ForEach(x => savedItems.Add(Save(x)));
            return savedItems;
        }

        public virtual bool Delete(int id)
        {
            _dictionary.Remove(id);
            return true;
        }

        public virtual bool DeleteAll()
        {
            _dictionary = new Dictionary<int, T>();
            return true;
        }

        protected virtual string GetIdentityName()
        {
            return ((MemberExpression)_identityExpression.Body).Member.Name;
        }

        protected virtual int GetIdentityValue(T item)
        {
            return (int) item.GetType().GetProperty(GetIdentityName()).GetValue(item, null);
        }

        protected virtual void SetIdentityValue(T item, object value)
        {
            item.GetType().GetProperty(GetIdentityName()).SetValue(item, value, null);
        }
    }

An Example of Usage

When using the base repository, we need some way to tell it which property will be used as the identity (key) on the entity object. The way I have provided is to specify a lambda expression, so that this is strongly typed. For example, if “Id” is the name of the identity property on the Account entity, then in AccountFakeRepository, you can provide a constructor like this:

public AccountFakeRepository() :  base(x=>x.Id)

This then tells the base fake repository to use the property “Id” as the identity or key.

Looking Closer at how the Identity is Resolved

Looking at the method “GetIdentityName” you can see that the identity property is retrieved from the right hand side of the lambda expression, i.e.:

return ((MemberExpression)_identityExpression.Body).Member.Name;

The methods “GetIdentityValue”, and “SetIdentityValue” are then used to get and set, repectively, the identity values using reflection, i.e.:

Get:

return (int) item.GetType().GetProperty(GetIdentityName()).GetValue(item, null);

Set:

item.GetType().GetProperty(GetIdentityName()).SetValue(item,
 value, null);

How to Test the Base Fake Repository Class

You may notice that the class is not marked as abstract, and there is a second constructor which has the dictionary as an additional parameter. Strictly speaking this should be an abstract class. However, I have written it this way in order toallow easy unit testing of the class. For example, in your unit test “SetUp” method, you can do something like this:

var accounts = GetFakeAccounts();
_baseFakeRepository = new BaseFakeRepository<Account>(x => x.Id, accounts);

You can then write a whole suite of tests against the BaseFakeRepository if you feel the need to prove that the fake methods work correctly.

Advertisement

No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.