2016-11-14 7 views
0

빌드해야하는 작은 응용 프로그램에 대한 단위 테스트를 빌드 중입니다.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에서 오버라이드 (override) 프록시 대를 사용하고 있습니다. 테스트가 실행되면 - 나는 저장소의 Get 메소드에 null 참조를 얻을 - 특히 쿼리 = DbSet을 : 난 그냥 UserManager 구현을 테스트하기 위해 노력하고

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

-하지 저장소 구현.

이 테스트를 올바르게 설정하는 방법은 무엇입니까?

답변

3

UserManager의 생성자에서 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