Created
July 20, 2011 18:57
-
-
Save alfeg/1095644 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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