3

正如标题中所说,我遵循模型优先方法。所以我的模型类是自动生成的。如果我想模拟包含实体类的DBContext派生类。阅读一些地方,为了进行单元测试,您需要将其更改为. 是否可以这样做,尤其是在我执行“运行自定义工具”时自动生成的类中是一个问题。但截至目前,我对其进行了修改。MyModelContainerDBSetsIDBSet

但真正的问题是:当我尝试 StubMyModelContainer返回一个从IDBSet. Rhino 模拟正在触发 InvalidOperationException:“无效调用,已使用最后一次调用,或未进行任何调用(确保您正在调用虚拟(C#)/可覆盖(VB)方法。”

这是我的单元测试代码。

MyModelContainer dbMock = MockRepository.GenerateMock<MyModelContainer>();
IDBSet<Models.MyEntity> entityMock = MockRepository.GenerateMock<IDBSet<Models.MyEntity>>()
dbMock.Stub( x=>x.MyEntities ).Return( entityMock );

最后一条语句触发了异常。我尝试使用此处IDBSet<>指定的假实现,但没有运气!

我使用 MVC 4,Rhino Mocks 3.6。任何帮助将不胜感激。

更新:

经过一些试验和研究,我找到了解决办法。我将代码更改为:

MyModelContainer dbMock = MockRepository.GenerateMock<MyModelContainer>();
IDBSet<Models.MyEntity> entityMock = MockRepository.GenerateMock<IDBSet<Models.MyEntity>>()
//dbMock.Stub( x=>x.MyEntities ).Return( entityMock );
dbMock.MyEntities = entityMock;

现在InvalidOperationException没有了。测试失败只是因为ExpectationViolationException这应该是正常的。

至于自动生成的模型类,发现编辑DbContext's T4 模板(.tt 扩展名)就可以了。感谢艾伦的博客

但我想知道为什么以前的代码不起作用。任何人?

4

3 回答 3

2

这里可能有2个原因:

  1. MyEntites的属性MyModelContainer不是虚拟的。
    在那种情况下,Rhino Mock 根本无法存根这个属性。然后dbMock.Stub(x=>x.MyEntities)会失败。

  2. MyEntites属性是虚拟的,但同时具有公共 getter 和公共 setter。
    那么符号dbMock.Stub(x=>x.MyEntities).Return(entityMock)是不允许的。你可以在这里看到解释。

在这两种情况下,正确的解决方法正是您所做的:使用dbMock.MyEntities = entityMock而不是dbMock.Stub(x=>x.MyEntities).Return(entityMock).

于 2013-03-20T20:19:53.260 回答
1

这是 Stubing(使用 RhinoMocks)IDbSet 返回 IQueryable 的扩展方法

public static class RhinoExtensions
{
    public static IDbSet<T> MockToDbSet<T>(this IQueryable<T> queryable) where T : class
    {
        IDbSet<T> mockDbSet = MockRepository.GenerateMock<IDbSet<T>>();
        mockDbSet.Stub(m => m.Provider).Return(queryable.Provider);
        mockDbSet.Stub(m => m.Expression).Return(queryable.Expression);
        mockDbSet.Stub(m => m.ElementType).Return(queryable.ElementType);
        mockDbSet.Stub(m => m.GetEnumerator()).Return(queryable.GetEnumerator());
        return mockDbSet;
    }
}

然后您现在可以像这样对 DbContext 存根:

_db.Stub(p => p.Customers).Return(fakeCustomers.MockToDbSet());
于 2016-03-02T12:13:34.563 回答
1

这是替换 IDbSet(使用 NSubstitute)返回 IQueryable 的扩展方法

    public static DbSet<T> FakeDbSet<T>(this IQueryable<T> queryable) where T : class
    {
        DbSet<T> fakeDbSet = Substitute.For<DbSet<T>, IQueryable<T>>();
        ((IQueryable<T>)fakeDbSet).Provider.Returns(queryable.Provider);
        ((IQueryable<T>)fakeDbSet).Expression.Returns(queryable.Expression);
        ((IQueryable<T>)fakeDbSet).ElementType.Returns(queryable.ElementType);
        ((IQueryable<T>)fakeDbSet).GetEnumerator().Returns(queryable.GetEnumerator());
        fakeDbSet.AsNoTracking().Returns(fakeDbSet);
        return fakeDbSet;
    }

然后您现在可以像这样对 DbContext 存根:

        var db = NSubstitute.Substitute.For<DataContext>();
        var fakeResult = emptyCustomers.FakeDbSet();
        db.Customers.Returns(fakeResult);
于 2016-08-31T12:33:02.550 回答