【问题标题】:Testing mocked EF context, context and unit of work with RhinoMocks and NUnit使用 RhinoMocks 和 NUnit 测试模拟的 EF 上下文、上下文和工作单元
【发布时间】:2012-12-04 19:30:58
【问题描述】:

我在模拟我的代码以使我能够测试我的 MVC 控制器时遇到了真正的问题。

我的仓库实现如下接口

public interface IEntityRepository<T>
{
    IQueryable<T> All { get; }
    IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties);
    void Delete(int id);
    T Find(int id);
    void InsertOrUpdate(T entity);
    void InsertOrUpdateGraph(T entity);
}

像这样

public interface IMonkeyRepository : IEntityRepository<Monkey>
{
}

我的 EF 上下文实现了以下接口

public interface IMonkeyContext
{
    IDbSet<Monkey> Monkeys { get; set; }
    DbEntityEntry Entry(object entity);
    IEnumerable<DbEntityValidationResult> GetValidationErrors();
    int SaveChanges();
}

我的工作单元接口是这样定义的

public interface IUnitOfWork<TContext> : IDisposable
{
    TContext Context { get; }
    int Save();
} 

并实施

public class MonkeyUnitOfWork : IUnitOfWork<IMonkeyContext>
{

    private readonly IMonkeyContext context;
    private bool disposed;
    public MonkeyUnitOfWork(IMonkeyContext context)
    {
        this.context = context;
    }
    public IMonkeyContext Context
    {
        get
        {
            return this.context;
        }
    }
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    public int Save()
    {
        var ret = this.context.SaveChanges();
        return ret;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                ((DbContext)this.context).Dispose();
            }
        }

        this.disposed = true;
    }
}

我有一个 MonkeyController,我想测试它的 Create 操作。我被定义了

        if (this.ModelState.IsValid)
        {
            this.repo.InsertOrUpdate(Mapper.Map<MonkeyModel, Monkey>(monkey));
            this.uow.Save();
            return this.RedirectToAction(MVC.Monkey.Index());
        }

        return this.View(monkey);

在我的单元测试中,我使用 RhinoMocks 并定义了测试

[TestFixture]
public class MonkeyControllerTests
{
    MockRepository mocks = null;

    private IMonkeyRepository monkeyRepository;

    private IMonkeyContext context;

    private MonkeyUnitOfWork unitOfWork;       

    private MonkeyController controller;

    [SetUp]
    public virtual void SetUp()
    {
        TestHelpers.SetupAutoMap();

        this.monkeyRepository = this.Mocks.StrictMultiMock<IMonkeyRepository>(typeof(IEntityRepository<Monkey>));

        this.context = this.Mocks.StrictMock<IMonkeyContext>();

        this.unitOfWork = new MonkeyUnitOfWork(this.context);

        this.controller = new MonkeyController(this.MonkeyRepository, this.unitOfWork);
    }

    [TearDown]
    public virtual void TearDown()
    {
        if (this.mocks != null)
        {
            try
            {
                this.mocks.ReplayAll();
                this.mocks.VerifyAll();
            }
            finally
            {
                this.mocks = null;
            }
        }
    }

    public MockRepository Mocks
    {
        get
        {
            if (mocks == null)
                mocks = new MockRepository();
            return mocks;
        }
    }

    [Test]
    public void MonkeyCreateShouldShouldDoSomeStuff()
    {
        var monkeyModel = ViewModelTestHelpers.CreateSingleMonkey();
        var monkey = Mapper.Map<MonkeyModel, Monkey>(monkeyModel);

        this.monkeyRepository.Expect(action => action.InsertOrUpdate(monkey));

        this.context.Expect(action => action.SaveChanges()).Return(1);
        var result = (RedirectToRouteResult)this.controller.Create(monkeyModel);

        Assert.AreEqual(MVC.Monnkey.ActionNames.Index, result.RouteValues["action"]);
    }
}

当我运行测试时,我会收到以下错误

上一个方法'IMonkeyContext.SaveChanges();'需要返回值或抛出异常。

或者它抱怨 IEntityRepository.InsertOrUpdate 预期 1 实际 0

我已经尝试了很多演员表和咒语来让它发挥作用,但我很难过。有谁知道如何正确模拟这些对象?或者如果我从根本上错过了什么?

【问题讨论】:

  • 只需删除 IRepository 的东西并进行集成测试(可能使用内存数据库)。您正在重新实现已经有效的东西。此外,当您使用将数据保存在内存中的 IDbSet 实现时,它将允许在您访问真实数据库时抛出的查询。
  • 我们将在周期的后期进行集成测试,但它是一个大型应用程序,具有很高的测试覆盖率,我们需要在合理的时间内运行测试。

标签: asp.net-mvc entity-framework repository-pattern unit-of-work rhino-mocks-3.5


【解决方案1】:

这似乎是一个小学生的错误

RhinoMocks 说 IEntityRepository.InsertOrUpdate 没有被调用时是正确的。

我在测试中从视图模型映射到模型的代码行

var monkey = Mapper.Map<MonkeyModel, Monkey>(monkeyModel);

然后在expect中使用它

this.monkeyRepository.Expect(action => action.InsertOrUpdate(monkey));

当然是告诉 Rhino 应该用这个猴子的确切实例来调用该函数。

该函数当然是在action内部以如下方式调用

this.repo.InsertOrUpdate(Mapper.Map<MonkeyModel, Monkey>(monkey));

不是同一个实例。

我现在已经转移到aaa语法并将测试代码更改为

this.monkeyRepository.Stub(r => r.InsertOrUpdate(Arg<Monkey>.Is.Anything));

和断言

this.monkeyRepository.AssertWasCalled(r => r.InsertOrUpdate(Arg<Monkey>.Is.Anything));

我现在要羞愧地低下头了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-31
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    • 2010-11-26
    相关资源
    最近更新 更多