これにはさまざまな方法があります。
命令バージョン
OPで提供されているものよりも単純な命令型バージョンを次に示します。
[Fact]
public void ImperativeTest()
{
var fixture = new Fixture();
var expected = fixture.CreateMany<SyncItem>(3).OrderBy(si => si.Key);
var unorderedItems = expected.Skip(1).Concat(expected.Take(1)).ToArray();
fixture.Inject(unorderedItems);
var sut = fixture.Create<SyncItemList>();
Assert.Equal(expected, sut);
}
多くの項目のデフォルト数は3ですが、このテスト ケースでは明示的に呼び出した方がよいと思います。ここで使用されるスクランブリング アルゴリズムは、3 つの (異なる) 要素のシーケンスを順序付けた後、最初の要素を後ろに移動すると順序付けられていないリストになるという事実を利用しています。
ただし、このアプローチの問題は、 の変更に依存しているfixture
ため、より宣言的なアプローチにリファクタリングするのが難しいことです。
カスタマイズ版
より宣言的なバージョンにリファクタリングするために、最初にスクランブリング アルゴリズムをCustomizationにカプセル化できます。
public class UnorderedSyncItems : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new UnorderedSyncItemsGenerator());
}
private class UnorderedSyncItemsGenerator : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var t = request as Type;
if (t == null ||
t != typeof(SyncItem[]))
return new NoSpecimen(request);
var items = ((IEnumerable)context
.Resolve(new FiniteSequenceRequest(typeof(SyncItem), 3)))
.Cast<SyncItem>();
return items.Skip(1).Concat(items.Take(1)).ToArray();
}
}
}
a の解決は、インスタンスnew FiniteSequenceRequest(typeof(SyncItem), 3))
の有限シーケンスを作成するための単純に弱い型付け (非ジェネリック) の方法です。それは舞台裏でSyncItem
何をするかです。CreateMany<SyncItem>(3)
これにより、テストを次のようにリファクタリングできます。
[Fact]
public void ImperativeTestWithCustomization()
{
var fixture = new Fixture().Customize(new UnorderedSyncItems());
var expected = fixture.Freeze<SyncItem[]>().OrderBy(si => si.Key);
var sut = fixture.Create<SyncItemList>();
Assert.Equal(expected, sut);
}
Freezeメソッドの使用に注意してください。UnorderedSyncItems
カスタマイズはSyncItem[]
インスタンスの作成方法のみを変更するため、これが必要です。要求を受け取るたびに、新しい配列を作成します。インスタンスを作成するFreeze
ときも、毎回同じ配列が再利用されるようにします。fixture
sut
慣習に基づくテスト
上記のテストは、[UnorderedConventions]
属性を導入することで、宣言型の規則ベースのテストにリファクタリングできます。
public class UnorderedConventionsAttribute : AutoDataAttribute
{
public UnorderedConventionsAttribute()
: base(new Fixture().Customize(new UnorderedSyncItems()))
{
}
}
UnorderedSyncItems
これは、カスタマイズを適用するための宣言的な接着剤です。テストは次のようになります。
[Theory, UnorderedConventions]
public void ConventionBasedTest(
[Frozen]SyncItem[] unorderedItems,
SyncItemList sut)
{
var expected = unorderedItems.OrderBy(si => si.Key);
Assert.Equal(expected, sut);
}
[UnorderedSyncItems]
および[Frozen]
属性の使用に注意してください。
このテストは非常に簡潔ですが、求めているものではないかもしれません。問題は、動作の変更が属性に隠されていること[UnorderedSyncItems]
です。そのため、何が起こっているのかはかなり暗黙的です。私は、テスト スイート全体に対して一連の規則として同じカスタマイズを使用することを好みます。そのため、このレベルでテスト ケースのバリエーションを導入することは好きではありません。ただし、規則でSyncItem[]
インスタンスが常に順序付けされていないことが規定されている場合は、この規則が適しています。
ただし、一部のテスト ケースで順序付けされていない配列のみを使用する場合、[AutoData]
属性の使用は最適な方法ではありません。
宣言型テスト ケース
属性と同じように、パラメーターレベルの属性を単純に適用できればいいと思い[Frozen]
ます-おそらくそれらを組み合わせて[Unordered][Frozen]
. ただし、このアプローチは機能しません。
前の例から、順序が重要であることに注意してください。フリーズする前に適用する必要がありますUnorderedSyncItems
。そうしないと、フリーズされる配列が順不同であることが保証されない可能性があるためです。
パラメーター レベルの属性の問題[Unordered][Frozen]
は、コンパイル中に、AutoFixture xUnit.net グルー ライブラリが属性を読み取って適用するときに、.NET フレームワークが属性の順序を保証しないことです。
代わりに、次のように、適用する単一の属性を定義できます。
public class UnorderedFrozenAttribute : CustomizeAttribute
{
public override ICustomization GetCustomization(ParameterInfo parameter)
{
return new CompositeCustomization(
new UnorderedSyncItems(),
new FreezingCustomization(parameter.ParameterType));
}
}
(属性FreezingCustomization
の基本的な実装を提供し[Frozen]
ます。)
これにより、次のテストを作成できます。
[Theory, AutoData]
public void DeclarativeTest(
[UnorderedFrozen]SyncItem[] unorderedItems,
SyncItemList sut)
{
var expected = unorderedItems.OrderBy(si => si.Key);
Assert.Equal(expected, sut);
}
この宣言型テストでは、パラメーター レベルで属性[AutoData]
によってスクランブリングが適用されるようになったため、カスタマイズなしでデフォルト属性を使用していることに注意してください。[UnorderedFrozen]
[AutoData]
これにより、派生属性にカプセル化された一連の (その他の) テスト スイート全体の規則を使用することも可能になり[UnorderedFrozen]
、オプトイン メカニズムとして使用することもできます。