【问题标题】:Customizing AutoFixure using FromSeed Causes Exception使用 FromSeed 自定义 AutoFixure 导致异常
【发布时间】:2015-11-10 16:39:29
【问题描述】:

给定两个类:

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。如果这发生在强类型构建表达式中,请尝试使用 IFactoryComposer 方法之一提供工厂。

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

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

【问题讨论】:

标签: c# unit-testing autofixture


【解决方案1】:

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

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

【讨论】:

  • 这就像一个魅力!感谢您的解决方法。
  • 我用 更好的 方法更新了自定义 RelaxedSeededFactory 样本生成器。它不会处理所有请求,而是只处理T 的种子和非种子请求。
  • 作为更新,AutoFixture 3.36.12 修复了这个问题:github.com/AutoFixture/AutoFixture/commit/…。谢谢@Enrico!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-22
  • 1970-01-01
  • 1970-01-01
  • 2016-09-17
  • 2011-01-16
相关资源
最近更新 更多