3

正如标题所说,我遵循模型第一的方法。所以我的Model类是自动生成的。如果我想模拟包含DBSets实体类的DBContext派生的MyModelContainer。阅读一下为了进行单元测试,你需要将其更改为IDBSet。是否有可能做到这一点,尤其是在我执行“运行自定义工具”时自动生成的类是一个问题。但截至目前我修改了它。嘲笑DBSet,EF模式第一

但真正的问题是:当我尝试存根MyModelContainer返回从IDBSet产生的模拟。 Rhino mock正在触发一个InvalidOperationException异常:“无效的调用,最后一次调用已经使用,或者没有调用(请确保您正在调用虚拟(C#)/ Overridable(VB)方法。”

我的单元测试代码。

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

最后陈述引发的异常。我试图使用指定here假执行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'sT4模板(.tt扩展名)将做的伎俩。感谢Alan's Blog

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

回答

2

2个原因都是可能的位置:中MyModelContainer

  1. MyEntites属性是不是虚拟的。
    在这种情况下,Rhino Mock根本不能残留这个属性。然后dbMock.Stub(x=>x.MyEntities)将失败。

  2. MyEntites属性是虚拟的,但同时具有公共getter和公共setter。
    然后表示法dbMock.Stub(x=>x.MyEntities).Return(entityMock)是不允许的。你可以看到解释here

在这两种情况下,正确的解决方法是你所做的事:用dbMock.MyEntities = entityMock代替dbMock.Stub(x=>x.MyEntities).Return(entityMock)

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()); 
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);