2016-11-14 28 views
0

我正在構建我們需要構建的小應用程序的單元測試。使用UnitOfWork嘲弄上下文和存儲庫

我已經實施了Repository/Unit Of Work模式。我的經理班實施工作單元模式。

對於給定的接口:

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>()); 

出於某種原因 - 被用來對從起訂量重寫的代理實際方法。當測試執行 - 我上庫的Get方法一空引用 - 特別是在查詢= 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

問題是你在AppManager的構造函數中傳遞了AppDbContext,這使得它依賴於它。反過來類是創建庫的內部情況,從而總是使用具體類:

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