Skip to content

Instantly share code, notes, and snippets.

@alfeg
Created July 20, 2011 18:57
Show Gist options
  • Save alfeg/1095644 to your computer and use it in GitHub Desktop.
Save alfeg/1095644 to your computer and use it in GitHub Desktop.
// Basic interfaces
/// <summary>
/// Wrapper of repository acces
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IDataRepository<T> where T : class
{
/// <summary>
/// Gets the underlying repository.
/// </summary>
IRepository<T> Repository { get; }
}
public interface IRepository<T> where T : class
{
IQueryable<T> Query();
T GetById(object id);
T Save(T entity);
void Delete(T entity);
int SaveChanges();
}
//Implementations
//Actual DB access
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveChanges();
}
public class EFRepository<T> : IRepository<T> where T : class
{
protected readonly IDbContext Context;
protected readonly IDbSet<T> Entities;
public EFRepository(IDbContext context)
{
this.Context = context;
this.Entities = context.Set<T>();
}
public virtual T GetById(object id)
{
return this.Entities.Find(id);
}
public T Save(T entity)
{
this.Entities.Add(entity);
return entity;
}
public int SaveChanges()
{
return this.Context.SaveChanges();
}
public virtual IQueryable<T> Query()
{
return this.Entities;
}
public virtual IDbSet<T> DbSet { get { return this.Entities; } }
public virtual void Delete(T entity)
{
this.Entities.Remove(entity);
}
}
//Datat access classes
public interface IUsersRepository : IDataRepository<User>
{
List<User> GetUsersWithStrangeName();
}
public class UserQueryRepository : IUserRepository
{
private readonly IRepository<User> userRepository;
public UserQueryRepository(IRepository<User> userRepository)
{
this.userRepository = userRepository;
}
public IRepository<User> Repository
{
get { return this.userRepository; }
}
public List<User> GetUsersWithStrangeName()
{
return this.Repository.Query().Where(user => user.Name.Contains("boooo")).ToList();
}
}
//Mocking
public void Test()
{
Mock<IRepository<T>> mock = this.moq.GetMock<IRepository<T>>();
mock.Setup(m => m.Query()).Returns(moqedList.AsQueryable());
mock.Setup(m => m.Save(It.IsAny<T>())).Returns((T item) =>
{
moqedList.Add(item);
return item;
});
mock.Setup(m => m.Delete(It.IsAny<T>())).Callback((T item) => moqedList.Remove(item));
//there is no GetById implementation. Implementing it's actually trivial
}
/// But I have some rambo skills in reflections... so
/// <summary>
/// Automaticaly mock and setups repositories mock based on lists
/// </summary>
public class RepositoriesMocker
{
private readonly AutoMoqer moq;
public RepositoriesMocker(AutoMoqer moqer)
{
this.moq = moqer;
}
protected void SetGenericRepositoryMock<TRepo, TImpl, T, TId>(List<T> items, Func<T, TId> getId)
where TRepo : class
where TImpl : TRepo
where T : class
{
GetGenericRepoMock(items, getId);
this.moq.SetInstance((TRepo) this.moq.Resolve<TImpl>());
}
/// <summary>
/// Sets the mocker for T entity
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item">The item.</param>
public void SetMockerFor<T>(T item) where T : class
{
SetMockerFor<T, Object>(item, null);
}
/// <summary>
/// Sets the mocker for T entity.
/// </summary>
/// <typeparam name="T">Type of entity</typeparam>
/// <typeparam name="TId">The type of the id property of Entity.</typeparam>
/// <param name="item">The item that need to be included in mocked list.</param>
/// <param name="getId">Function that return id property for etity. Default is entity => entity.Id</param>
public void SetMockerFor<T, TId>(T item, Func<T, TId> getId = null) where T : class
{
SetMockerForList<T, TId>(new List<T> {item});
}
/// <summary>
/// Sets the mocker for List of T entities.
/// </summary>
/// <typeparam name="T">Type of entity</typeparam>
/// <param name="items">The items list that need to be included in mock.</param>
public void SetMockerForList<T>(IList<T> items) where T : class
{
SetMockerForList<T, Object>(items);
}
protected Func<T, TId> ByIdProperty<T, TId>()
{
Type entityType = typeof (T);
PropertyInfo property = entityType.Properties("Id").FirstOrDefault();
if (property == null) return null;
return (ent) => (TId) property.Get(ent);
}
/// <summary>
/// Sets the mocker for List of T entities.
/// </summary>
/// <typeparam name="T">Type of entity</typeparam>
/// <typeparam name="TId">The type of the id property of Entity.</typeparam>
/// <param name="items">The items list that need to be included in mock.</param>
/// <param name="getId">Function that return id property for etity. Default is entity => entity.Id</param>
public void SetMockerForList<T, TId>(IList<T> items, Func<T, TId> getId = null) where T : class
{
if (getId == null)
getId = ByIdProperty<T, TId>();
Type entityType = typeof (T);
IList<Type> repos = Assembly.GetAssembly(entityType).TypesImplementing<IDataRepository<T>>();
if (repos == null || repos.Count == 0)
throw new ArgumentException("No types implementing IDataRepository<" + entityType.Name + "> found");
Type repo = repos.Where(el => el.IsInterface).SingleOrDefault();
Type impl = repos.Where(el => el.IsInterface == false).SingleOrDefault();
if (repo == null) throw new ArgumentException(string.Format("Interface implementing IRepository<{0}> and IDataRepository<{0}> not found", entityType.Name));
if (impl == null) throw new ArgumentException(string.Format("Type implementing IRepository<{0}> and IDataRepository<{0}> not found", entityType.Name));
MethodInfo method = GetType().Method("SetGenericRepositoryMock").MakeGenericMethod(repo, impl, entityType, typeof (TId));
method.Call(this, items, getId);
}
protected Mock<IRepository<T>> GetGenericRepoMock<T, TId>(List<T> moqedList, Func<T, TId> getId = null) where T : class
{
moqedList = moqedList ?? new List<T>();
Mock<IRepository<T>> mock = this.moq.GetMock<IRepository<T>>();
mock.Setup(m => m.Query()).Returns(moqedList.AsQueryable());
mock.Setup(m => m.Save(It.IsAny<T>())).Returns((T item) =>
{
moqedList.Add(item);
return item;
});
if (getId != null)
mock.Setup(el => el.GetById(It.IsAny<TId>()))
.Returns((TId id) => moqedList.Where(el => IsIdEqual(getId(el), id)).SingleOrDefault());
mock.Setup(m => m.Delete(It.IsAny<T>())).Callback((T item) => moqedList.Remove(item));
return mock;
}
private static bool IsIdEqual<TId>(TId value, TId id)
{
Type type = typeof (TId);
if (type.IsClass)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null && id == null)
{
return true;
}
if (value == null || id == null)
{
return false;
}
// ReSharper restore CompareNonConstrainedGenericWithNull
}
return value.Equals(id);
}
}
/// The usage of this awesommness is
public void Test()
{
var moqer = new AutoMoq();
var repoMocker = new RepositoriesMocker(moqer);
var users = new List<User>();
remoMocker.SetMockerForList(users, user => user.Id);
// now you can use code with users repositories
var classUsingUserUserQueryRepository = moqer.Resolve<UserService>();
classUsingUserUserQueryRepository.ListUsers(); //return users above
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment