使用Iunitofwork和IgenericRepository进行Moq测试

时间:2013-12-05 12:21:40

标签: unit-testing model-view-controller repository-pattern unit-of-work xunit

我已经在一些教程之后实现了UnitOfWork和GenericRepository。

我有IEFDbContext / EFDbContext类来处理数据库,我的IUnitofWork如下...

public interface IUnitOfWork : IDisposable
{
    IGenericRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
    void Save();
}

和IGenericRepository如下

public interface IGenericRepository<T> where T : class
{
    IQueryable<T> Get();
    T GetByID(object id);
    void Add(T entity);
    void Delete(T entity);
    void DeleteAll(IEnumerable<T> entity);
    void Update(T entity);
    bool Any();
}

我的控制器如下......

public class ProjectController : Controller
{
    private IGenericRepository<Project> ProjectRepo { get; set; }

    private IUnitOfWork _unitOfWork { get; set; }

    public ProjectController(IUnitOfWork uow)
    {
     _unitOfWork = uow;
     ProjectRepo = _unitOfWork.GetRepository<Project>();
    }
}

我的创建操作如下

[HttpPost]
public ActionResult Create(AddProjectModel model)
{
    if (ModelState.IsValid)
    {
        ProjectRepo.Add(newProject);
        _unitOfWork.Save();
    }
 }

当我运行应用程序时,一切正常,我知道为什么要使用IuitofWork和GenericRepository,这就是为什么我不创建IProjectRepository然后在这里注入...

我的问题是单元测试此操作。

我在我的Test项目中创建了MockGenericRepository和MockUnitofWork,如下所示......

public class MockUnitOfWork<TContext> : IUnitOfWork where TContext : class, new()
{
    private TContext _ctx;
    private Dictionary<Type, object> _repositories;

    public MockUnitOfWork()
    {
        _ctx = new TContext();
        _repositories = new Dictionary<Type, object>();
    }

    public IGenericRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        if (_repositories.Keys.Contains(typeof(TEntity)))
        {
            return _repositories[typeof(TEntity)] as IGenericRepository<TEntity>;
        }

        var entityName = typeof(TEntity).Name;
        var prop = _ctx.GetType().GetProperty(entityName);
        MockRepository<TEntity> repository = null;
        if (prop != null)
        {
            var entityValue = prop.GetValue(_ctx, null);
            repository = new MockRepository<TEntity>(entityValue as List<TEntity>);
        }
        else
        {
            repository = new MockRepository<TEntity>(new List<TEntity>());
        }
        _repositories.Add(typeof(TEntity), repository);
        return repository;
    }

    public void SetRepositoryData<TEntity>(List<TEntity> data) where TEntity : class
    {
        IGenericRepository<TEntity> repo = GetRepository<TEntity>();

        var mockRepo = repo as MockRepository<TEntity>;
        if (mockRepo != null)
        {
            mockRepo._context = data;
        }
    }

    public void Save()
    {

    }

    public void Dispose()
    {
    }
}

MockGenericRepository如下

public class MockRepository<T> : IGenericRepository<T> where T : class
{
    public List<T> _context;

    public MockRepository(List<T> ctx)
    {
        _context = ctx;
    }

    public IQueryable<T> Get()
    {
        return _context.AsQueryable();
    }

    public T GetByID(object id)
    {
      //  return _context.Find(s => s.Id ==  id).SingleOrDefault();
        throw new NotImplementedException();
    }

    public virtual void Add(T entity)
    {
        _context.Add(entity);
    }

    public virtual void Delete(T entity)
    {
        _context.Remove(entity);
    }

    public virtual void DeleteAll(IEnumerable<T> entity)
    {
        _context.RemoveAll(s => s == entity);
    }

    public virtual void Update(T entity)
    {
        var entry = _context.Where(s => s == entity).SingleOrDefault();
        entry = entity;
    }

    public virtual bool Any()
    {
        return _context.Any();
    }
}

我的ProjectControllerTest如下......

public class ProjectControllerTest
{
    private readonly List<ALCProject> _projectsList;
    private readonly IUnitOfWork _mockU = new MockUnitOfWork<EFDbContext>();

    private ProjectController GetControllerObject()
    {
        foreach (var project in _projectsList)
        {
            _mockU.GetRepository<Project>().Add(project);
        }

        var controller = new ProjectController(_mockU);
        return controller;
    }

[Fact]
public void TestCreateProject()
{
        var controller = GetControllerObject();
        var result = controller.Create(new AddProjectModel());
        Assert.Equal(_mockU.GetRepository<Project>().Get().Count(),4);
}

我遇到的问题是我的测试确实通过但是当我查看_mockU.GetRepository()时.Get()我可以看到添加了一个新项目,但“ID”字段为0,我理解的原因是这是因为我的MockGenericRepsoiotry将上下文定义为public List _context;这就是为什么它只是在列表中添加新项目。

有人可以指导我如何才能生成新ID我觉得我必须伪造EFDbContext但我不知道怎么做???

1 个答案:

答案 0 :(得分:1)

项目的Id为0,因为没有任何东西可以将其设置为其他任何内容!实体的Id属性很可能映射到数据库中的标识列(SQL Server作为猜测)。调用DbContext.Save()时,它在SQL中执行插入操作。 SQL Server正在为该行生成一个新的唯一Id值,并将其返回到Entity Framework。 EF然后将值填充回它持久存在的对象中。

你的模拟工作单元实际上并没有调用DbContext.Save(),尽管它正在使用EF上下文作为它返回的存储库的数据存储。您可以通过在MockUnitOfWork<T>.Save()方法中放置代码来为任何存储库中值为零的任何Id属性提供数字来增强它。

或者,正如你所说,这是一个单元测试,所以也许你不应该关心。

我不明白为什么你的工作单元模拟正在查看Entity Framework上下文。为什么不总是根据List<TEntity>

返回存储库