使用FromSeed自定义AutoFixure导致异常

本文关键字:异常 AutoFixure FromSeed 自定义 使用 | 更新日期: 2023-09-27 18:21:19

给定两个类:

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:

修饰的ISpecimenBuilder无法基于请求创建样本:Foo。如果请求表示一个接口或抽象类,就会发生这种情况;如果是这种情况,请注册一个ISpecimenBuilder,它可以根据请求创建样本。如果这种情况发生在强类型Build表达式中,请尝试使用IFactoryComposer方法之一提供工厂。

我希望TestFooFactory在创建Bar的过程中传递一个null种子值,就像我创建没有种子值的Foo一样。我是做错了什么,还是这可能是一个bug?

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

使用FromSeed自定义AutoFixure导致异常

自定义Fixture以使用种子值的方式是正确的。

您看到的行为是FromSeed自定义如何修改AutoFixture管道的结果。如果你有兴趣了解细节,我在这里已经描述过了。

作为一种变通方法,您可以为种子请求使用自定义样本生成器,如以下所示:

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的属性时,此自定义将把default(Foo)(即null)作为种子传递给TestFooFactory工厂函数。