嘲讽DBSet,EF模型优先(Mocking DBSet, EF Model First)

2019-08-17 18:40发布

正如标题所说,我按照模型第一种方法。 所以我的模型类是自动生成的。 如果我想嘲笑DBContext派生MyModelContainer包含DBSets实体类的。 看了一些地方,为了进行单元测试,则需要将其更改为IDBSet 。 无论是其能够做到这一点尤其是在一类获取,当我做“运行自定义工具”自动生成的一个问题。 但截至现在,我修改了它。

但真正的问题是:当我尝试存根MyModelContainer返回从生成的模拟IDBSet 。 犀牛模拟射击是一个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,犀牛制品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扩展)会做的伎俩。 由于艾伦的博客

但我想知道为什么前面的代码没有工作。 任何人?

Answer 1:

2个原因可能在这里:

  1. MyEntites财产MyModelContainer不虚。
    在这种情况下犀牛模拟不能在所有存根此属性。 然后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)



Answer 2:

下面是用于取代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);


Answer 3:

下面是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());


文章来源: Mocking DBSet, EF Model First