使用自动混合创建结构不会引发公共构造函数错误

时间:2012-10-17 09:17:07

标签: c# unit-testing autofixture

我有一个结构例如:

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();

因为它与问题无关。

2 个答案:

答案 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>的呼叫有效。

如果有更多开箱即用的解决方案,请发布,我会接受它作为答案。