web-dev-qa-db-ja.com

フリーズ/注入/登録の違いを理解できません

始める前に、私はAutoFixtureの大ファンですが、まだツールの使用方法を習得中です。 Autofixture Mr Ploehとすべての貢献者を開発してくれてありがとう。

それでは、私の質問から始めましょう。

によると、AutoFixture/AutoMoqは注入されたインスタンス/凍結されたモックを無視します

上記のリンクの興味深い部分には、このコードが与えられています

Mock<ISettings> settingsMock = new Mock<ISettings>();
settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);

ISettings settings = settingsMock.Object;
fixture.Inject(settings);

どのマークの回答に書き直すことができます

fixture.Freeze<Mock<ISettings>>()
       .Setup(s => s.Get(settingKey)).Returns(xmlString);

これは構文糖のように見えます。Freezeメソッドを使用すると、モックの作成、構成、autofixtureコンテナーへのインジェクションを流暢なインターフェイスで書き込むことができます。

Webでいくつかの調査を行った後、実際にはFreezeとInjectの間に機能的な違いがあります。私はこの質問を見つけました: https://github.com/AutoFixture/AutoFixture/issues/59 これは AutoFixtureでnullインスタンスをフリーズするにはどうすればよいですか

上記のリンクの作成者は、Freezeメソッドを次のように説明しています。

内部的には、Freezeは要求されたタイプのインスタンス(IPayPalConfigurationなど)を作成し、それを注入して、再度要求したときに常にそのインスタンスを返すようにします

私たちがするとき

var customer = fixture.Freeze<Order>();

コードがOrderタイプを要求するときは常に、同じOrderのインスタンスを使用します。しかし、Freezeコンストラクターで特定のインスタンスを使用するように指定した場合はどうなりますか?

これは小さなコード例です:

[Fact]
public void MethodeName()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    fixture.Freeze<OrderLine>(new OrderLine("Foo"));
    var order = fixture.Create<Order>();
}

public class Order
{
    private readonly OrderLine _line;

    public Order(OrderLine line)
    {
        _line = line;
    }
}
public class OrderLine
{
    private readonly string _name;

    public OrderLine(string name)
    {
        _name = name;
    }
}

OrderLineの名前は、namefe48163a-d5a0-49a5-b349-7b11ba5f804bの代わりに「Foo」と等しくないはずです。 Freezeメソッドのドキュメンテーションは言う:

<typeparam name="T">The type to freeze.</typeparam>
<param name="fixture">The fixture.</param>
<param name="seed">Any data that adds additional information when creating the anonymous object. Hypothetically, this value might be the value being frozen, but this is not likely.</param>

なぜ値がいつ返されるのか、作成者はわからないのですか? Freezeのコンストラクターでインスタンスを指定した場合、autofixtureがこのインスタンスを使用することを期待していますか?

その後

これを行うようにカスタマイズしていない限り、は固定値として使用される可能性が低いことに注意してください。特定の値をFixtureに注入する場合は、代わりにメソッドを使用する必要があります。`

シードパラメータをカスタマイズする必要があるようです。誰でも明確にできますか?ドキュメントで指摘されている解決策は、Injectメソッドを使用することです。そして実際、これはOrderLineを使用したコード例で機能します。

Freeze、Inj​​ect、およびRegisterの違いを理解するためのヘルプを探しています。ソースコードによると、RegisterはInjectメソッドによって呼び出されますが、ラムダを必要とします。

45
John

登録して挿入

むかしむかし、InjectFreezeもありませんでした。 Registerがコードを支配しました。

当時、このように定義されたRegisterオーバーロードがありました:

_public static void Register<T>(this IFixture fixture, T item)
_

ただし、APIをこの親戚と共有する必要がありました。

_public static void Register<T>(this IFixture fixture, Func<T> creator)
_

AutoFixtureの作成者は、これは良いことだと思っていましたが、悲しいことに、ユーザーは混乱に悩まされました。最も悲惨なことに、ユーザーは次のように書くことができます。

_fixture.Register(() => universe.LightUp());
_

だけでなく

_fixture.Register(universe.LightUp);
_

_universe.LightUp_はメソッドへの参照であり、デリゲートに一致するため、これはまったく同じことを意味します。

ただし、その構文はプロパティ参照のように見えるため、LightUpがメソッドではなくプロパティであった場合、最初のオーバーロードがコンパイラによって選択されます。

これにより多くの混乱が生じたため、Register<T>(this IFixture fixture, T item)オーバーロードはInject<T>(this IFixture fixture, T item)に名前が変更されました。

凍結

フリーズには別の歴史があります。ずっと昔、私がまだ命令的な方法でAutoFixtureを使用していたとき、私は次のようなコードを繰り返し書いていることに気づきました:

_var foo = fixture.Create<Foo>();
fixture.Inject(foo);
_

だから私はこれがコンセプトだと決めてFreezeと名付けました。 Freezeメソッドは、これらの2行のコードの省略形にすぎません。

Freeze、Inj​​ect、およびRegisterの違いを理解するための助けを探しています。ソースコードによると、Injectメソッドによって呼び出されますが、ラムダが必要です。

一般に、InjectRegisterを区別することは、署名が衝突しないため、それほど難しくありません。したがって、これらの2つの方法のいずれかで目標を達成しようとし、コードがコンパイルされる場合、おそらく正しいバージョンを選択します。

これは、OPで使用されるオーバーロードに対応していない場合のFreezeの場合にも当てはまります。

_[EditorBrowsable(EditorBrowsableState.Never)]
public static T Freeze<T>(this IFixture fixture, T seed)
_

このオーバーロードには実際に_EditorBrowsableState.Never_が含まれていることに注意してください。これは常に人を混乱させるためです。しかし、それにもかかわらず、明らかに人々はまだその過負荷を見つけているので、 AutoFixture 4で移動する必要があります と思います。実装が簡単だったために存在する機能の1つです...

47
Mark Seemann

FreezeInject、およびRegisterはすべて、作成アルゴリズムをカスタマイズしています。

InjectRegisterを使用して、例ではオブジェクトを特定の方法で作成する必要があることをexplicitlyと指定していますnew OrderLine("Foo")を手動で指定する。

Freezeを使用すると、オブジェクトの作成方法howを指定するのではなく、AutoFixtureにインスタンスを提供するように要求します。

最後に、上記のすべてのメソッドは同じ下位レベルのAPIを使用します。

fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());


fixture.Freeze<OrderLine>(new OrderLine("Foo"));が指定されたシード値でOrderLineインスタンスを作成しない理由は、 デフォルトではシードは無視される であるためです。

特定のタイプのシード値を優先するために、SeedFavoringRelay<T>を作成できます。

public class SeedFavoringRelay<T> : ISpecimenBuilder where T : class
{
    public object Create(object request, ISpecimenContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var seededRequest = request as SeededRequest;
        if (seededRequest == null || !seededRequest.Request.Equals(typeof(T)))
            return new NoSpecimen(request);

        var seed = seededRequest.Seed as T;
        if (seed == null)
            return new NoSpecimen(request);

        return seed;
    }
}

次に、以下のように使用できます。

fixture.Customizations.Add(
    new SeedFavoringRelay<OrderLine>());

fixture.Freeze<OrderLine>(new OrderLine("Foo"));
// -> Now fixture.Create<Order>() creates an Order with OrderLine's Name = "Foo".
15
Nikos Baxevanis

私はあなたのテストを修正しました(現時点では何もアサートしていません、BTW)。その実行をステップ実行すると、プライベートな_lineメンバー値がOrderLineに注入されるため、Orderに "Foo"が表示されます。

これらのオブジェクトについてアサーションを作成できるように、OrderLineOrderNameOrderLineの読み取り専用プロパティを追加したテストの別のバージョンがありましたが、ここにもありません。

このテストでは、FromFactoryメソッドを直接使用してフィクスチャーを設定します。

[Fact]
public void MethodName()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    const string expected = "Foo";
    fixture.Customize<OrderLine>(o => o
        .FromFactory(() =>
            new OrderLine(expected)));
    var order = fixture.Create<Order>();
}
1
Jeff