我有一个结构例如:
public struct Foo
{
public int Bar;
public string Baz;
}
并希望使用autofixture在我的单元测试中创建它。我尝试使用以下内容:
IFixture fixture = new Fixture();
var f = fixture.CreateAnonymous<Foo>();
但这会引发AutoFixture was unable to create an instance
错误。是否可以使用自动混合自动创建结构?怎么可以这样做?请注意,我正在处理遗留代码,因此需要处理结构。 :)
编辑:
已更改
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());
要
IFixture fixture = new Fixture();
因为它与问题无关。
答案 0 :(得分:10)
这实际上是C#编译器如何处理值类型的假象。虽然the documentation seems to indicate otherwise,但从反射的角度来看,Foo结构没有公共构造函数。
E.g。如果你执行这段代码:
var ctors = typeof(Foo).GetConstructors();
结果是一个空数组。
但是,此代码编译:
var f = new Foo();
所以你可以说AutoFixture应该能够创建一个Foo实例。
然而,最后,mutable structs are evil应该不惜一切代价避免。更好的选择是将Foo实现更改为:
public struct Foo
{
public Foo(int bar, string baz)
{
this.Bar = bar;
this.Baz = baz;
}
public readonly int Bar;
public readonly string Baz;
}
如果这样做,您现在不仅拥有(更多)正确的值类型,而且AutoFixture还能够创建实例而无需进一步修改。
因此,这是GOOS范例的一个很好的例子,您应该听取您的测试。如果它们存在摩擦,则可能是有关生产代码的反馈。在这种情况下,这正是你要用脚射击自己的反馈,因为价值类型的实现是有缺陷的。
P.S。即使你“修复”上面概述的Foo结构,有什么意义使它成为结构而不是类?它仍然包含一个字符串(引用类型)字段,因此即使结构本身将存在于堆栈中,字符串字段仍将指向堆中的数据。
我添加了this issue作为AutoFixture的新功能。
答案 1 :(得分:1)
从这个blog post中获取提示,我创建了一个实现ISpecimenBuilder
class FooBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var sr = request as SeededRequest;
if (sr == null)
{
return new NoSpecimen(request);
}
if (sr.Request != typeof(Foo))
{
return new NoSpecimen(request);
}
var foo = new Foo();
foo.Bar = context.CreateAnonymous<int>();
foo.Baz = context.CreateAnonymous<string>();
return foo;
}
}
并将该类添加为自定义
fixture.Customizations.Add(new FooBuilder());
使CreateAnonymous<Foo>
的呼叫有效。
如果有更多开箱即用的解决方案,请发布,我会接受它作为答案。