【问题标题】:How to mockup Entity Framework 6 With Moq & Autofixture如何使用 Moq 和 Autofixture 模拟 Entity Framework 6
【发布时间】:2013-10-11 22:27:51
【问题描述】:

我正在使用AutoMoq,但我有点困惑如何编写我的第一个单元测试,因为 Entity Framework(首先使用 EF6 和代码)dbContext

// in service class(constructor)
private readonly MyContext context;

public PriceService(MyContext context)
{
    this.context = context;
}

// following would be in nunit test method.
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var priceService = fixture.Create<PriceService>();

当我运行单元测试时它崩溃了

在 Ploeh.AutoFixture.Kernel.TerminatingSpecimenBuilder.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.AutoPropertiesCommand`1.Execute(对象标本,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Collections.Generic.List`1..ctor(IEnumerable`1 集合) 在 System.Linq.Enumerable.ToList[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.MethodInvoker.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.c__DisplayClass6.b__1(ISpecimenBuilder b) 在 System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 在 System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 在 System.Linq.Enumerable.d__a5`1.MoveNext() 在 System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 源) 在 Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,ISpecimenContext 上下文) 在 Ploeh.AutoFixture.SpecimenFactory.Create[T](ISpecimenContext 上下文,T 种子) 在 Ploeh.AutoFixture.SpecimenFactory.Create[T](ISpecimenContext 上下文) 在 PriceServiceTests.cs:line 26 中的 PriceServiceTests.Test_Price_Object_Setup()

编辑

在 EF 6 中,它们似乎使 DbSet 更加可模拟。

https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20May%2016%2c%202013

使 DbSet 更可模拟

  • 这意味着添加受保护的构造函数并使方法成为虚拟方法
  • 请注意,从使用受保护构造函数的 DbSet 派生的类型将创建一个不绑定到任何上下文的对象,并且方法将是无操作的。从创建测试替身的角度来看,这使得它非常类似于 IDbSet。
  • 如果我们采用此选项,我们可能会废弃 IDbSet
  • 值得注意的是,没有发现在功能上与使用 IDbSet 进行测试替身不同的情况。但是,社区中有一种强烈的感觉,即界面是首选。

有人知道如何模拟吗?

编辑 2

我找到了this article,但它一直在崩溃

public class MyContext : DbContext
{
    //public GroceryListContext()
    //    : base()
    //{

    //}
    public virtual DbSet<Price> Prices { get; set; }
}

[Test]
public void Test_Price_Object_Setup_Properly()
{
    var mockContext = new Mock<MyContext>();

    var mockSet = new Mock<DbSet<Price>>(); // had to add EF to my test solution.
    mockContext.Setup(m => m.Prices).Returns(mockSet.Object);
    var service = new PriceService(mockContext.Object);

    // dies when using autofixture so thought try first moq like in article
   //var priceService = fixture.Create<PriceService>();

   Assert.That(true, Is.EqualTo(false));
}

以下例外:

MyContext.Tests.Services.PriceServiceTests.Test_If_Price_Object_Setup_Properly: System.ArgumentException : Type to mock must be an interface or an abstract or non-sealed class. ----&gt; System.TypeLoadException : Method 'Create' on type 'DbSet1Proxyb409fc6b430b4568aac048b60ea2f02e' 从程序集'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=a621a9e7e5c32e66

【问题讨论】:

  • 相关:stackoverflow.com/questions/16696777/…stackoverflow.com/questions/14391146/…autofixture.codeplex.com/discussions/262557 问题:您是否发现 EF6 实际上的行为与 5 不同,或者您只是想要完整?
  • 您提供的最后一个示例,没有 AutoFixture 的示例适用于我的机器。我已经复制了您的 PriceService、MyContext 类和 Test_Price_Object_Setup_Properly 方法。我实现了我自己的 Price 类,因为你没有提供它并且它运行时没有任何异常。我正在使用实体框架 6.0 RC 1 和 Visual Studio 2013 RC。
  • @RubenBartelink 好吧,我的问题从如何模拟 Datacontext 开始,然后我发现 EF 可以在 dbset 上进行模拟并且不需要包装器。我在做教程时遇到了很大的问题。我现在知道问题出在 autofixture 上,它安装了 3.0 的最小起订量,但本教程需要 4.0 才能正常工作。因此,我不确定现在是否可以使用 autofixture。
  • @OlavNybø - 是的,它现在可以在我的机器上运行,因为您可能刚刚从它的 nuget 包中直接安装了 moq。我从 autofixture 得到它,它安装了版本 3,你需要 moq 版本 4 才能使那段代码工作。
  • @chobo 很高兴您现在可以使用它,并且 obv 不好,它花费了您时间到达那里。一般来说,AutoFixture 取决于 需要通过其测试的最早版本。如果您需要 Moq v4,因为您有一个具有奇怪依赖项的库。 AutoFixture.Xunit 是否需要 1.9.2,因为 1.8 不适用于 Tech Z ? IOW 我认为重要和有用的是 AF 不会强制依赖并非绝对必要的东西(例如 .NET 4 上的 3.0+ dep 最近让我刮了将近一天的牦牛毛 - 但我有机会投票决定 dep 是否还可以

标签: c# entity-framework nunit moq autofixture


【解决方案1】:

您需要提供一个specification,它表示应该模拟DbSet&lt;T&gt; 类(尽管它不是抽象类型或接口)。

原因是DbSet&lt;T&gt; 类是公共的,但它有一个protected 构造函数。

规格

internal class DbSetTypeSpecification : IRequestSpecification
{
    public bool IsSatisfiedBy(object request)
    {
        var type = request as Type;
        if (type == null)
            return false;

        return type.IsGenericType
            && typeof(DbSet<>) == type.GetGenericTypeDefinition();
    }
}

示例

[Fact]
public void Test()
{
    var fixture = new Fixture();
    fixture.ResidueCollectors.Add(
        new MockRelay(
            new DbSetTypeSpecification()));

    Assert.DoesNotThrow(() => 
        fixture.Create<PriceService>());
}

现在 AutoFixture 可以提供自动生成的PriceService 值。


请注意MyContext 类也是公共的,而且,AFAICT,它也有一个公共构造函数。这意味着 AutoFixture 默认不会为 MyContext 类提供自动模拟实例。

(如果你能提供你的场景,我可能会提供进一步的帮助。)

【讨论】:

  • 您是否尝试过针对 EF 6 进行此操作?我发现没有什么对我有用的原因是 AutoFixture 安装了最小起订量 3,而我需要起订量 4 才能使教程工作。我试图保留最小起订量 4 和自动夹具,但后来我永远无法“使用”导入夹具。
  • 好吧,我只是在调查 Autofixture,以前从未真正使用过它。我想设置我的测试,这样我就不必手动创建所有的模拟(使用最小起订量)和虚拟数据。这就是我想要做的。当我需要模拟中的某些数据成为某个值时,我也有点不清楚(比如 PriceObject 中的价格必须是 5)。
  • @chobo2 关于起订量请阅读我的回答here
  • Nikos,即使其他人的点击更珍贵,你已经从我这里获得了 +1 :) @chobo2 NB 请确保您查看各种 SO Q & As 重新生成返回的测试数据通过 Moqed 接口(Moq 本质上不提供全局挂钩返回值生成的方法,因此您需要了解何时/如何/在何处可以为每个测试提供适当的自定义)。 (其中许多帖子通常都涉及到 EF——它经常让人们停止思考如何隔离问题。确保测试始终以正确的原因失败至关重要——尤其是自动模拟)
  • @NikosBaxevanis - 是的,我看到了你的帖子,我知道为什么它对我不起作用。我告诉 nuget 不要获得依赖项,其中包括 Autofixture,所以它只有 AutoFixture.moq,所以这就是为什么不导入命名空间的原因。
【解决方案2】:

有一个名为 AutoFixture.AutoEF 的 NuGet 包可以解决您的问题

fixture.Customize(new EntityCustomization(new DbContextEntityTypesProvider(typeof(MyContext))));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-24
    • 2014-01-30
    • 2020-02-18
    • 2018-01-15
    • 2014-08-29
    • 2014-11-07
    • 1970-01-01
    • 2017-07-25
    相关资源
    最近更新 更多