7

次のカスタマイズを作成し、ほとんどのテストでコンポジットの一部として適用しました。私のエンティティには読み取り専用の 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 から直接継承するエンティティの 1 つをテストにフィードすると、すべて問題なく、書き込み可能なプロパティが自動入力されます。ただし、BaseEntity からさらに下にあるものから継承するエンティティを要求すると、カスタマイズによってプロパティの自動入力が妨げられます。

このテスト メソッドの User エンティティは適切に入力されています。

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 に欠けているものがありますか? 最初、途中、最後にカスタマイズを適用しましたが、役に立ちませんでした。

4

1 に答える 1

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の意見は次のとおりです。

クラス継承よりもオブジェクトコンポジションを優先します。

-デザインパターン、p。20

AutoFixtureは何よりもまずTDDツールであり、TDDの主な利点の1つは、クラス設計に関するフィードバックを提供することです。この場合、フィードバックは次のとおりです。継承は厄介で面倒です。デザインを再考してください。

于 2013-02-03T20:15:51.920 に答える