我们有带实体框架的MVC4项目用于存储。 对于我们的测试,我们最近开始使用Autofixture,它非常棒。
我们的模型图非常深,通常通过AutoFixture创建一个对象创建整个图: Person - >团队 - >部门 - >公司 - >合同 - > .... 等。
问题在于时间。 对象创建最多需要一秒。这导致测试缓慢。
我发现自己做了很多事情就是这样:
var contract = fixture.Build<PersonContract>()
.Without(c => c.Person)
.Without(c => c.PersonContractTemplate)
.Without(c => c.Occupation)
.Without(c => c.EmploymentCompany)
.Create<PersonContract>();
这很有效,很快。但是这种过度规范使得测试难以阅读,有时我会在不重要的.With(c => c.PersonId, 42)
列表中忽略.Without()
等重要细节。
所有这些被忽略的对象都是Entity Framework的导航属性,并且都是虚拟的。
是否有全球方式告诉AutoFixture忽略虚拟成员?
我尝试创建ISpecimentBuilder
,但没有运气:
public class IgnoreVirtualMembers : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if (request.GetType().IsVirtual // ?? this does not exist )
{
return null;
}
}
}
我似乎无法在ISpecimenBuilder
中找到一种方法来检测我们正在构建的对象是另一个类中的虚拟成员。可能ISpecimenBuilder
这不是正确的地方。还有其他想法吗?
答案 0 :(得分:21)
在Mark的博客上阅读更多内容(this particularly)我找到了做我想要的方法:
/// <summary>
/// Customisation to ignore the virtual members in the class - helps ignoring the navigational
/// properties and makes it quicker to generate objects when you don't care about these
/// </summary>
public class IgnoreVirtualMembers : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
var pi = request as PropertyInfo;
if (pi == null)
{
return new NoSpecimen(request);
}
if (pi.GetGetMethod().IsVirtual)
{
return null;
}
return new NoSpecimen(request);
}
}
您可以将这些内容包装到自定义中:
public class IgnoreVirtualMembersCustomisation : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new IgnoreVirtualMembers());
}
}
所以在你的测试中你只是这样做:
var fixture = new Fixture().Customize(new IgnoreVirtualMembersCustomisation());
然后去创建你的复杂模型。
享受!
答案 1 :(得分:3)
我遇到了同样的问题,并决定更进一步,为延迟加载导航属性创建自定义。该项目位于Github和NuGet。
考虑下面的简单对象图,它具有循环依赖性:
class Foo
{
public int Id { get; set; }
public int BarId { get; set; }
public virtual Bar Bar { get; set; }
}
class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
}
通过此自定义,调用var foo = fixture.Create<Foo>()
将创建类型为Foo
的对象。调用foo.Bar
getter将使用DynamicProxy和AutoFixture动态创建Bar
的实例并将其分配给该属性。对foo.Bar
的后续调用将返回相同的对象。
N.B。自定义设置不够聪明,无法设置foo.Bar.Foo = foo
- 必须在需要时手动完成