使用 FromSeed 自定义 AutoFixure 导致异常

Customizing AutoFixure using FromSeed Causes Exception

给定两个 类:

class Foo
{
    ...
}

class Bar
{
    public Foo FooBar { get; set; }
}

我设置了以下测试:

void Test()
{
    var fixture = new Fixture();

    fixture.Customize<Foo>(x => x.FromSeed(TestFooFactory));

    var fooWithoutSeed = fixture.Create<Foo>();
    var fooWithSeed = fixture.Create<Foo>(new Foo());

    var bar = fixture.Create<Bar>(); //error occurs here
}

Foo TestFooFactory(Foo seed)
{
    //do something with seed...

    return new Foo();
}

我可以直接使用和不使用种子值创建 Foo 个对象,没有任何问题。但是一旦我尝试创建一个具有 Foo 属性 的 Bar 对象,我就会得到一个 ObjectCreationException:

The decorated ISpecimenBuilder could not create a specimen based on the request: Foo. This can happen if the request represents an interface or abstract class; if this is the case, register an ISpecimenBuilder that can create specimens based on the request. If this happens in a strongly typed Build expression, try supplying a factory using one of the IFactoryComposer methods.

我希望 TestFooFactory 在创建 Bar 期间传递 null 种子值,就像我创建 Foo 时没有种子值一样。我做错了什么,或者这可能是一个错误?

在我的真实场景中,我想自定义当我传入种子值时 AutoFixture 如何为某些对象使用种子值,但我仍然希望 AutoFixture 在没有提供种子时默认为正常行为。

您自定义 Fixture 以使用种子值的方式

您看到的行为是 FromSeed 自定义修改 AutoFixture 管道的结果。如果您有兴趣阅读详细信息,我已经对其进行了描述 here

作为一种解决方法,您可以使用自定义样本生成器来处理像这样的种子请求:

public class RelaxedSeededFactory<T> : ISpecimenBuilder
{
    private readonly Func<T, T> create;

    public RelaxedSeededFactory(Func<T, T> factory)
    {
        this.create = factory;
    }

    public object Create(object request, ISpecimenContext context)
    {
        if (request != null && request.Equals(typeof(T)))
        {
            return this.create(default(T));
        }

        var seededRequest = request as SeededRequest;

        if (seededRequest == null)
        {
            return new NoSpecimen(request);
        }

        if (!seededRequest.Request.Equals(typeof(T)))
        {
            return new NoSpecimen(request);
        }

        if ((seededRequest.Seed != null)
            && !(seededRequest.Seed is T))
        {
            return new NoSpecimen(request);
        }

        var seed = (T)seededRequest.Seed;

        return this.create(seed);
    }
}

然后您可以使用它来创建 Foo 类型的对象,如下所示:

fixture.Customize<Foo>(c => c.FromFactory(
    new RelaxedSeededFactory<Foo>(TestFooFactory)));

当填充 Foo.[=21= 类型的属性时,此自定义将传递 default(Foo) – 即 null – 作为 TestFooFactory 工厂函数的种子]