2016-11-14 6 views
0

Я строю модульные тесты для небольшого приложения, которое нам нужно построить.Mocking Context and Repository с UnitOfWork

Я реализовал шаблон хранилища/единицы работы. Мои классы менеджера реализуют единицу шаблона работы.

Для данного интерфейса:

public interface IUserManager 
{ 
    List<ApplicationUser> GetUsers(Expression<Func<ApplicationUser, bool>> filter = null); 
    ApplicationUser GetUser(Expression<Func<ApplicationUser, bool>> filter); 
    ApplicationUser AddUser(string username, List<string> environmentIds, bool isAdmin = false); 
    void DeleteUser(string username); 
    ApplicationUser UpdateUser(string id, List<string> environmentIds, bool isAdmin = false); 
    IList<string> GetUserRoles(string id); 
} 

Я реализованного

public class UserManager : IUserManager 
{ 

    #region private fields 

    private readonly IRepository<ApplicationUser> _userRepository; 
    private readonly IRepository<Application> _applicationRepository; 
    private readonly IRepository<Role> _roleRepository; 
    private readonly IActiveDirectoryManager _activeDirectoryManager; 


    #endregion 

    #region ctor 

    public UserManager(AppDbContext context, IActiveDirectoryManager activeDirectoryManager) 

    { 
     _activeDirectoryManager = activeDirectoryManager; 
     _userRepository = new Repository<ApplicationUser>(context); 
     _applicationRepository = new Repository<Application>(context); 
     _roleRepository = new Repository<Role>(context); 
    } 

    #endregion 


    #region IUserManager 

    public ApplicationUser AddUser(string username, List<string> applicationIds, bool isAdmin = false) 
    { 
     //Get the environments in the list of environmentIds 
     var applications = _applicationRepository.Get(e => applicationIds.Contains(e.Id)).ToList(); 

     //Get the user from AD 
     var user = _activeDirectoryManager.GetUser(username); 

     //set the Id 
     user.Id = Guid.NewGuid().ToString(); 

     //add the environments to the user 
     applications.ForEach(x => 
     { 
      user.Applications.Add(x); 
     }); 

     //if the user is an admin - retrieve the role and add it to the user 
     if (isAdmin) 
     { 
      var role = _roleRepository.Get(r => r.Name == "admin").FirstOrDefault(); 
      if (role != null) 
      { 
       user.Roles.Add(role); 
      } 
     } 

     //insert and save 
     _userRepository.Insert(user); 
     _userRepository.Save(); 

     //return the user 
     return user; 

    } 

//removed for brevity 
} 

Мой блок тестовый класс:

[TestClass] 
public class UserManagerUnitTest 
{ 
    private readonly Mock<IActiveDirectoryManager> _adManager; 
    private readonly IUserManager _userManager; 
    private readonly Mock<IRepository<Application>> _applicationRepository; 
    private readonly Mock<IRepository<ApplicationUser>> _userRepository; 
    private readonly Mock<IRepository<Role>> _roleRepository; 


    public UserManagerUnitTest() 
    { 
     var context = new Mock<AppDbContext>(); 
     _adManager = new Mock<IActiveDirectoryManager>(); 

     _applicationRepository = new Mock<IRepository<Application>>(); 
     _userRepository = new Mock<IRepository<ApplicationUser>>(); 
     _roleRepository = new Mock<IRepository<Role>>(); 

     _userManager = new UserManager(context.Object, _adManager.Object); 

    } 

    [TestMethod] 
    [TestCategory("AddUser"), TestCategory("Unit")] 
    public void AddUser_ValidNonAdmin_UserIsAdded() 
    { 
     #region Arrange 

     string username = "testUser"; 
     List<string> applicationIds = new List<string>() {"1", "2", "3"}; 

     _applicationRepository.Setup(x => x.Get(It.IsAny<Expression<Func<Application, bool>>>(), 
      It.IsAny<Func<IQueryable<Application>, IOrderedQueryable<Application>>>(), It.IsAny<string>())) 
      .Returns(new List<Application>()); 

     _adManager.Setup(x => x.GetUser(It.IsAny<string>())).Returns(new ApplicationUser()); 


     #endregion 

     #region Act 

     var result = _userManager.AddUser(username, applicationIds, false); 

     #endregion 

     #region Assert 
     Assert.IsNotNull(result); 
     Assert.IsFalse(result.IsAdmin); 
     #endregion 
    } 

} 

И, наконец, интерфейс хранилища:

public interface IRepository<TEntity> where TEntity : class 
{ 
    IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, 
     Func<IQueryable<TEntity> , IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = ""); 

    TEntity GetById(object id); 
    void Insert(TEntity entity); 
    void Delete(object id); 
    void Delete(TEntity entityToDelete); 
    void Update(TEntity entityToUpdate); 
    void Save(); 

} 

И реализация:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class 
{ 
    private readonly AppDbContext _context; 
    internal DbSet<TEntity> DbSet; 

    public Repository(AppDbContext context) 
    { 
     _context = context; 
     DbSet = _context.Set<TEntity>(); 
    } 

    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, 
     Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "") 
    { 
     IQueryable<TEntity> query = DbSet; 

     if (filter != null) 
      query = query.Where(filter); 

     foreach (var prop in includeProperties.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries)) 
     { 
      query = query.Include(prop); 
     } 

     if (orderBy != null) 
     { 
      return orderBy(query).ToList(); 
     } 
     else 
     { 
      return query.ToList(); 
     } 


    } 

    public virtual TEntity GetById(object id) 
    { 
     return DbSet.Find(id); 
    } 

    public virtual void Insert(TEntity entity) 
    { 
     DbSet.Add(entity); 
    } 

    public virtual void Delete(object id) 
    { 
     TEntity entityToDelete = DbSet.Find(id); 
     Delete(entityToDelete); 
    } 

    public void Get(Expression<Func<Application, bool>> expression, Func<IQueryable<Application>> func, IOrderedQueryable<Application> orderedQueryable) 
    { 
     throw new NotImplementedException(); 
    } 

    public virtual void Delete(TEntity entityToDelete) 
    { 
     if (_context.Entry(entityToDelete).State == EntityState.Detached) 
     { 
      DbSet.Attach(entityToDelete); 
     } 
     DbSet.Remove(entityToDelete); 
    } 

    public virtual void Update(TEntity entityToUpdate) 
    { 
     DbSet.Attach(entityToUpdate); 
     _context.Entry(entityToUpdate).State = EntityState.Modified; 
    } 

    public void Save() 
    { 
     _context.SaveChanges(); 
    } 
} 

моя проблема заключается в макете IRepository<Application>

  _applicationRepository.Setup(x => x.Get(It.IsAny<Expression<Func<Application, bool>>>(), 
      It.IsAny<Func<IQueryable<Application>, IOrderedQueryable<Application>>>(), It.IsAny<string>())) 
      .Returns(new List<Application>()); 

По какой-то причине - фактический метод используется в сравнении с перегруженной прокси из Moq. Когда тест выполняется - я получаю пустую ссылку на Получ хранилища - в частности, на запрос = DbSet:

public Repository(AppDbContext context) 
    { 
     _context = context; 
     DbSet = _context.Set<TEntity>(); 
    } 

    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, 
     Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "") 
    { 
     IQueryable<TEntity> query = DbSet; **//null here because db should be** mocked 

     if (filter != null) 
      query = query.Where(filter); 

Я пытаюсь проверить только реализацию UserManager - не реализации хранилища.

Что было бы правильным способом установить этот тест?

ответ

3

Проблема в том, что вы передаете AppDbContext в конструкторе UserManager, что делает его зависимым от него. Класс, в свою очередь, создают внутренние экземпляры из хранилищ, таким образом всегда с использованием конкретных классов:

public UserManager(AppDbContext context, IActiveDirectoryManager activeDirectoryManager) 
{ 
    _activeDirectoryManager = activeDirectoryManager; 
    _userRepository = new Repository<ApplicationUser>(context); 
    _applicationRepository = new Repository<Application>(context); 
    _roleRepository = new Repository<Role>(context); 
} 

Вы должны вместо абстрактного из создания хранилищ и изменить конструктор так, что он принимает экземпляр на основе интерфейсы:

public UserManager(IRepository<ApplicationUser> userRepository, IRepository<Application> applicationRepository, IRepository<Role> roleRepository, IActiveDirectoryManager activeDirectoryManager) 
{ 
    _activeDirectoryManager = activeDirectoryManager; 
    _userRepository = userRepository; 
    _applicationRepository = applicationRepository; 
    _roleRepository = roleRepository; 
} 

таким образом, вы можете абстрагироваться из хранилищ, так что ваши издевается используются вместо реальных классов.

+0

Вот как я изначально имел это. Однако я столкнулся с проблемой сохранения, потому что все хранилища имели разные экземпляры контекста. Я использую autofac, поэтому я изменил регистрацию UserManager, чтобы построить каждый репозиторий экземпляр с тем же экземпляром моего контекста db. Очистила его. Благодарю. – JDBennett

+0

Ну, если это так, вы должны сделать шаг назад и еще раз взглянуть на ваш дизайн. Хранилища не должны иметь зависимости друг от друга. Если у вас проблемы с сохранением, это, вероятно, означает, что у вас есть некоторое перекрытие, которое нужно реорганизовать. – JuanR