如何使用 Moq 和自动夹具模拟实体框架 6



我正在使用AutoMoq,但由于实体框架(使用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.NKernel.TerminatingSpecimenBuilder.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixtureKernel.Postprocessor`1.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixtureKernel.Postprocessor`1.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.AutoPropertiesCommand`1.Execute(对象样本,ISpecimeContext上下文)位于Ploeh.AutoFixtureKernel.Postprocessor`1.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixtureKernel.Postprocessor`1.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixtureKernel.Postprocessor`1.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Collections.Generic.List`1.ctor(IEnumerable`1集合)位于System.Linq.Enumerable.ToList[TSource](IEnumerable`1源)位于Ploeh.AutoFixtureKernel.MethodInvoker.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixtureKernel.Postprocessor`1.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixtureKernel.Postprocessor`1.Create(对象请求,ISpecimeContext上下文)位于Ploeh.AutoFixture.Cernel.CompositeSpecimenBuilder.c__DisplayClass6.b_1(ISpecimenBuilder b)位于System.Linq.Enumerable.WhereSelectArrayTerator`2.MoveNext()位于System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()位于System.Linq.Enumerable.d_a5`1.MoveNext()位于System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1源)位于Ploeh.AutoFixture.Cernel.RecursionGuard.Create(对象请求,ISpecimeContext上下文)在Ploeh.AutoFixture.SpecimenFactory.Create[T](ISpecimeContext上下文,T种子)在Ploeh.AutoFixture.SpecimenFactory.Create[T](ISpecimeContext上下文)在PriceServiceTests.cs:line 26中的PriceServiceTests.Test_Price_Object_Setup()处编辑

在EF 6中,他们似乎正在使DbSet更具可模仿性。

https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%2016年5月20日%2c%2013

使DbSet更易于模拟

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

有人知道如何模拟它吗?

编辑2

我找到了这篇文章,但它不断崩溃

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_Operationly:System.ArgumentException : Type to mock must be an interface or an abstract or non-sealed class. ----> System.TypeLoadException : Method 'Create' on type 'DbSet1Proxy409fc6b430b4568aac048b60ea2f02e'来自程序集DynamicProxyGenAssembly2,Version=0.0.0.0,Culture=neutral,PublicKeyToken=a621a9e7e5c32e69'试图隐式重写具有较弱类型参数约束的方法'。

您需要提供一个规范,指示DbSet<T>类应该被模拟(尽管它不是抽象类型或接口)。

原因是DbSet<T>类是公共的,但它有一个受保护的构造函数。

规格

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类提供自动模拟实例。

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

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

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

最新更新