如标题中所述,我遵循Model First方法。所以我的Model类是自动生成的。如果我想要模拟包含DBContext
实体类的MyModelContainer
派生DBSets
。阅读一些内容以便进行单元测试,您需要将其更改为IDBSet
。是否可以这样做,特别是在我执行“运行自定义工具”时自动生成的类是一个问题。但截至目前我修改了它。
但真正的问题是:当我尝试使用Stub 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 );
最后一个语句是触发异常。我尝试使用IDBSet<>
指定here的虚假实现,但没有运气!
我使用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
,这应该是正常的。
对于自动生成的Model类,我们发现编辑DbContext's
T4模板(.tt扩展名)可以解决问题。感谢Alan's Blog
但我想知道为什么以前的代码不起作用。任何人吗?
答案 0 :(得分:2)
这里有两个理由:
MyEntites
MyModelContainer
属性不是虚拟的
在那种情况下,Rhino Mock根本不能存在这个属性。然后dbMock.Stub(x=>x.MyEntities)
将失败。
MyEntites
属性是虚拟的,但同时包含公共getter和public setter
然后不允许使用符号dbMock.Stub(x=>x.MyEntities).Return(entityMock)
。你可以看到解释,例如here
在这两种情况下,正确的解决方案正是您所做的:使用dbMock.MyEntities = entityMock
代替dbMock.Stub(x=>x.MyEntities).Return(entityMock)
。
答案 1 :(得分: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());
答案 2 :(得分:1)
Here is an extension method for Substituting IDbSet (with NSubstitute) to return an 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;
}
Then you can now stub the DbContext like this:
var db = NSubstitute.Substitute.For<DataContext>();
var fakeResult = emptyCustomers.FakeDbSet();
db.Customers.Returns(fakeResult);