【问题标题】:Mocking DBSet, EF Model First模拟 DBSet,EF 模型优先
【发布时间】:2013-03-20 06:13:36
【问题描述】:

正如标题中所说,我遵循模型优先方法。所以我的模型类是自动生成的。如果我想模拟包含DBSets 实体类的DBContext 派生MyModelContainer。阅读一些内容以进行单元测试,您需要将其更改为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&lt;&gt; 指定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

但我想知道为什么前面的代码不起作用。有人吗?

【问题讨论】:

    标签: unit-testing asp.net-mvc-4 nunit entity-framework-5 rhino-mocks


    【解决方案1】:

    这里可能有两个原因:

    1. MyModelContainerMyEntites 属性不是虚拟的。
      在那种情况下,Rhino Mock 根本无法存根这个属性。那么dbMock.Stub(x=&gt;x.MyEntities) 就会失败。

    2. MyEntites 属性是虚拟的,但同时具有公共 getter 和公共 setter。
      那么符号dbMock.Stub(x=&gt;x.MyEntities).Return(entityMock) 是不允许的。你可以看到解释,例如here.

    在这两种情况下,正确的解决方法正是您所做的:使用 dbMock.MyEntities = entityMock 而不是 dbMock.Stub(x=&gt;x.MyEntities).Return(entityMock)

    【讨论】:

      【解决方案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);
      

      【讨论】:

        【解决方案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());
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-17
          • 2019-06-10
          • 2011-08-13
          • 2012-08-25
          • 2021-04-07
          • 2011-07-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多