10

我有一个结构,例如:

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错误。是否可以使用 autofixture 自动创建结构?怎么可能做到这一点?请注意,我正在处理遗留代码,因此需要处理结构。:)

编辑 :

改变了

IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());

IFixture fixture = new Fixture();

因为它与问题无关。

4

2 回答 2

11

这本质上是 C# 编译器如何处理值类型的人工制品。虽然文档似乎另有说明,但从反射的角度来看, Foo 结构没有公共构造函数。

例如,如果您执行此代码:

var ctors = typeof(Foo).GetConstructors();

结果是一个空数组。

但是,此代码编译:

var f = new Foo();

所以你可以争辩说 AutoFixture 应该能够创建 Foo 的一个实例。

然而,最终,可变结构是邪恶的,应该不惜一切代价避免。更好的选择是将 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范式的一个很好的例子,你应该听听你的测试。如果它们存在摩擦,则可能是有关您的生产代码的反馈。在这种情况下,这正是由于值类型实现存在缺陷而导致您即将自取其辱的反馈。

PS即使您像上面概述的那样“修复” Foo 结构,将其设为结构而不是类有什么意义?它仍然包含一个字符串(引用类型)字段,因此即使结构本身将存在于堆栈中,字符串字段仍将指向堆中的数据。


我已将此问题添加为 AutoFixture 的一个可能的新功能。

于 2012-10-18T04:11:22.267 回答
1

从这篇博文中得到启发,我创建了一个类来实现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>工作。

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

于 2012-10-17T14:31:40.090 回答