Unit testing 模拟数据库集,EF模型优先
正如标题中所说,我遵循模型优先的方法。所以我的模型类是自动生成的。如果我想模拟Unit testing 模拟数据库集,EF模型优先,unit-testing,asp.net-mvc-4,nunit,entity-framework-5,rhino-mocks,Unit Testing,Asp.net Mvc 4,Nunit,Entity Framework 5,Rhino Mocks,正如标题中所说,我遵循模型优先的方法。所以我的模型类是自动生成的。如果我想模拟DBContext派生的MyModelContainer,它包含实体类的dbset。阅读其中一些内容,为了进行单元测试,您需要将其更改为IDBSet。特别是在我运行“运行自定义工具”时自动生成的类中,是否可以这样做是一个值得关注的问题。但是现在我修改了它 但真正的问题是:当我试图存根MyModelContainer以返回从IDBSet生成的模拟时。Rhino mock正在触发InvalidOperationExcept
DBContext
派生的MyModelContainer
,它包含实体类的dbset
。阅读其中一些内容,为了进行单元测试,您需要将其更改为IDBSet
。特别是在我运行“运行自定义工具”时自动生成的类中,是否可以这样做是一个值得关注的问题。但是现在我修改了它
但真正的问题是:当我试图存根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 );
MyModelContainer dbMock=MockRepository.GenerateMock();
IDBSet entityMock=MockRepository.GenerateMock()
Stub(x=>x.MyEntities).Return(entityMock);
最后一条语句正在触发异常。我尝试使用指定的IDBSet
的伪实现,但没有成功
我使用MVC4,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;
MyModelContainer dbMock=MockRepository.GenerateMock();
IDBSet entityMock=MockRepository.GenerateMock()
//Stub(x=>x.MyEntities).Return(entityMock);
dbMock.MyEntities=entityMock;
现在,InvalidOperationException
消失了。
测试失败的唯一原因是ExpectationViolationException
,这应该是正常的
至于自动生成的模型类,我们发现编辑DbContext的T4模板(.tt扩展名)就可以了
但是我想知道为什么前面的代码不起作用。有人吗?这里可能有两个原因:
MyModelContainer
的MyEntites
属性不是虚拟的。
在这种情况下,Rhino Mock根本无法存根这个属性。那么dbMock.stub(x=>x.MyEntities)
将失败
MyEntites
属性是虚拟的,但同时具有公共getter和公共setter。
然后表示法dbMock.Stub(x=>x.MyEntities).Return(entityMock)
是不允许的。您可以查看解释,例如
在这两种情况下,正确的修复方法正是您所做的:使用dbMock.MyEntities=entityMock
而不是dbMock.Stub(x=>x.MyEntities).Return(entityMock)
下面是一个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;
}
}
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;
}
下面是一个扩展方法,用于替换IDbSet(使用NSubstitute)以返回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;
}
}
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;
}
publicstaticdbset FakeDbSet(这个IQueryable查询表),其中T:class
{
DbSet fakeDbSet=替换.For();
((IQueryable)fakeDbSet.Provider.Returns(queryable.Provider);
((IQueryable)fakeDbSet.Expression.Returns(queryable.Expression);
((IQueryable)fakeDbSet.ElementType.Returns(queryable.ElementType);
((IQueryable)fakeDbSet.GetEnumerator().Returns(queryable.GetEnumerator());
AsNoTracking().Returns(fakeDbSet);
返回fakeDbSet;
}
然后,现在可以按如下方式存根DbContext:
_db.Stub(p => p.Customers).Return(fakeCustomers.MockToDbSet());
var db = NSubstitute.Substitute.For<DataContext>();
var fakeResult = emptyCustomers.FakeDbSet();
db.Customers.Returns(fakeResult);
var db=NSubstitute.Substitute.For();
var fakeResult=emptyCustomers.FakeDbSet();
db.客户.退货(fakeResult);