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