为什么AutoFixture Customization导致继承的属性无法填充?

时间:2013-02-01 02:59:51

标签: unit-testing autofixture

我编写了以下自定义,并在大多数测试中将其作为复合的一部分应用。我的实体有一个只读ID,但我在这个自定义中使用他们的SetId方法来确保所有实体都有一些Id,如果它们是瞬态的(没有Id)。

public class SetEntityIdCustomization : ICustomization {
    public void Customize(IFixture fixture) {
        var engine = ((Fixture)fixture).Engine;
        fixture.Customizations.Add(new Postprocessor(
            engine, o => {
                var entity = o as BaseEntity;
                if (entity == null || !entity.IsTransient()) {
                    return;
                }
                entity.SetId(fixture.CreateAnonymous<Guid>());
            }));
    }
}

这一直很有效,直到今天我发现了一件非常奇怪的事情。如果我提供一个直接从BaseEntity继承的实体的测试,一切都很好,并且它的可写属性是自动填充的。但是,如果我要求从BaseEntity继承的实体继承,我的自定义会阻止属性自动填充。

此测试方法中的用户实体已正确填写:

public class User : BaseEntity {
    public string Email { get; set; }
    public int CoolThings { get; set; }
}

...
[Theory, AutoDomainData]
public void SomeTest(User user, ...) {
    // user.Email and user.CoolThings have auto-filled values, as expected.
    ...
}

但是,以下测试中的AwesomeUser实体不会自动填充任何相同的属性。

public class AwesomeUser : User {
    ...
}

...
[Theory, AutoDomainData]
public void SomeOtherTest(AwesomeUser user, ...) {
    // user.Email nor user.CoolThings have auto-filled values. What gives?
    ...
}

在两个测试用例中,由于我的自定义,Id属性是自动填充的。如果我删除自定义,SomeOtherTest的AwesomeUser实例会自动填充其继承的属性。我必须假设我的定制是弄乱的东西。

有没有更好的方法让我所有的BaseEntity实例设置他们的ID,或者是否有其他我在AutoFixture中缺少的东西?我首先在中间,最后一次应用我的自定义,但无济于事。

1 个答案:

答案 0 :(得分:6)

上面提供的解决方案是一个非常聪明的尝试,但不是我以前见过的。一个更惯用的解决方案是这样的:

public void Customize(IFixture fixture)
{
    fixture.Customizations.Add(
        new FilteringSpecimenBuilder(
            new Postprocessor(
                new BaseEntityBuilder(
                    new ConstructorInvoker(
                        new ModestConstructorQuery())),
                new AutoPropertiesCommand().Execute),
            new BaseEntitySpecification()));
}

private class BaseEntityBuilder : ISpecimenBuilder
{
    private readonly ISpecimenBuilder builder;
    private readonly IRequestSpecification specification;

    public BaseEntityBuilder(ISpecimenBuilder builder)
    {
        this.builder = builder;
        this.specification = new BaseEntitySpecification();
    }

    public object Create(object request, ISpecimenContext context)
    {
        if (!this.specification.IsSatisfiedBy(request))
            return new NoSpecimen(request);

        var b = (BaseEntity)this.builder.Create(request, context);
        b.SetId((Guid)context.Resolve(typeof(Guid)));
        return b;
    }
}

private class BaseEntitySpecification : IRequestSpecification
{
    public bool IsSatisfiedBy(object request)
    {
        var t = request as Type;
        if (t == null)
            return false;

        if (!typeof(BaseEntity).IsAssignableFrom(t))
            return false;

        return true;
    }
}

正如您所看到的,这不是一个简单的单行,这表明AutoFixture是一个相当自以为是的库。在这种情况下,AutoFixture的意见是:

  

赞成对象组合而不是类继承。

     

- Design Patterns,p。 20

AutoFixture首先是TDD工具,TDD的主要优势之一是它提供有关类设计的反馈。在这种情况下,反馈是:继承是尴尬和麻烦。重新考虑设计。