使用UnitOfWork模拟上下文和存储库 [英] Mocking Context and Repository with UnitOfWork

查看:64
本文介绍了使用UnitOfWork模拟上下文和存储库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我们需要构建的小应用程序构建单元测试.

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模拟上下文和存储库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆