我正在使用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 'DbSet
1Proxy409fc6b430b4568aac048b60ea2f02e'来自程序集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))));