问题描述
我正在为我们需要构建的小应用程序构建单元测试.
I am building out unit tests for a small app we need to build.
我已经实现了存储库/工作单元模式.我的经理班实现了工作单元模式.
I have implemented the Repository / Unit Of Work pattern. My manager classes implement the unit of work pattern.
对于给定的接口:
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中覆盖的代理.执行测试时-在存储库的Get方法上获得空引用-特别是在查询= DbSet上:
For some reason - actual method is being used versus the overridden proxy from Moq. When the test executes - I get a null reference on the Get method of the repository - specifically on the query = 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实现,而不是存储库实现.
I am trying to test just the UserManager implementation - not the repository implementation.
设置此测试的正确方法是什么?
What would be the correct way to set this test up?
推荐答案
问题是您要在UserManager的构造函数中传递AppDbContext,这使其依赖于它.该类依次创建存储库的内部实例,因此使用具体的类始终:
The issue is you are passing the AppDbContext in the constructor of UserManager, which makes it dependent on it. The class in turn is creating internal instances of the repositories, thus always using the concrete classes:
public UserManager(AppDbContext context, IActiveDirectoryManager activeDirectoryManager)
{
_activeDirectoryManager = activeDirectoryManager;
_userRepository = new Repository<ApplicationUser>(context);
_applicationRepository = new Repository<Application>(context);
_roleRepository = new Repository<Role>(context);
}
相反,您应该抽象出存储库的创建并修改构造函数,以使其采用基于接口的实例:
You should instead abstract out the creation of the repositories and modify the constructor so that it takes an instance based on the interfaces:
public UserManager(IRepository<ApplicationUser> userRepository, IRepository<Application> applicationRepository, IRepository<Role> roleRepository, IActiveDirectoryManager activeDirectoryManager)
{
_activeDirectoryManager = activeDirectoryManager;
_userRepository = userRepository;
_applicationRepository = applicationRepository;
_roleRepository = roleRepository;
}
通过这种方式,您可以抽象出存储库,以便使用模拟而不是真实的类.
This way you are able to abstract out the repositories so your mocks are used instead of the real classes.
这篇关于使用UnitOfWork模拟上下文和存储库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!