2013-07-25 25 views
6

確定的なテストをセットアップして、リスト内の項目の順序を確認する方法を教えてください。リスト内の項目のユニットテスト順序

最初に私は次のようでした:

public void SyncListContainsSortedItems(
    [Frozen] SyncItem[] expected, 
    SyncItemList sut) 
{ 
    Assert.Equal(expected.OrderBy(x => x.Key).First(), sut.First()); 
} 

しかし、すべての良いテストと同じように、私が最初に私のコードを変更する前に故障を探しました。もちろん、運が成功すると成功し、その後は失敗しました。だから私の初期の失敗は決定論的ではありません。

public void SyncListContainsSortedItems(
    [Frozen] SyncItem[] seed, 
    SyncItemList sut) 
{ 
    var expected = seed.OrderByDescending(x => x.Key); 
    Assert.Equal(expected.OrderBy(x => x.Key).First(), sut.First()); 
} 

驚いたことに、それはまた、決定論的故障を提供していませんでした:

第二に、私は、次の、思考、「確かにこれは失敗を保証する」を行いました。凍った種子が自然に降順で作成されるかもしれないので、私はそれが実現したので、実際には状況を改善しなかったからです。

現時点では、私の実装では、コンストラクタを通過するアイテムは順序付けされません。テストのためのベースラインを確立するにはどうすればよいですか?

追加情報シンクアイテムリストのコードを以下に示します。それはデザインであると私は模索しています。そのあまりない:

public class SyncItemList : List<SyncItem> 
{ 
    public SyncItemList(SyncItem[] input) 
    { 
     foreach (var item in input) { this.Add(item); } 
    } 
} 

を更新私がテストを開発してきました。次のは、で動作しますが、冗長性が高いです。それが通過するために私の実装を変更する

public void SyncListContainsSortedItems(IFixture fixture, List<SyncItem> seed) 
{ 
    var seconditem = seed.OrderBy(x => x.Key).Skip(1).First(); 
    seed.Remove(seconditem); 
    seed.Insert(0, seconditem); 
    var seedArray = seed.ToArray(); 

    var ascending = seedArray.OrderBy(x => x.Key).ToArray(); 
    var descending = seedArray.OrderByDescending(x => x.Key).ToArray(); 
    Assert.NotEqual(ascending, seedArray); 
    Assert.NotEqual(descending, seedArray); 

    fixture.Inject<SyncItem[]>(seedArray); 
    var sut = fixture.Create<SyncItemList>(); 

    var expected = ascending; 
    var actual = sut.ToArray(); 
    Assert.Equal(expected, actual); 
} 

1つの簡単な方法は、SortedSet<SyncItem>代わりのList<SyncItem>を継承することです。

+0

「SyncItemList」はどのように見えますか? –

+0

属性とUnOrderedリクエストタイプ、有限シーケンスのようなものがあれば、本当にクールだと思います。 3つ以上のユニークなアイテムが必要なだけで、順序が乱れることがあります。 – cocogorilla

+0

どのバージョンのAutoFixtureを使用しているのですか? AutoFixture 3の[数値はランダムです](https://github.com/AutoFixture/AutoFixture/wiki/AutoFixture-3.0-Release-Notes#numbers-are-random) –

答えて

10

これについて移動する様々な方法があります。ここで

命令型バージョン

はOPで提供1よりも簡単不可欠バージョンです:the default number of many items is 3ながら

[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(別個の)要素のシーケンスを順序付けするという事実を利用します。

しかし、このアプローチの問題点は、fixtureの変更に依存しているため、より宣言的なアプローチにリファクタリングすることが難しいことです。(

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(); 
     } 
    } 
} 

new FiniteSequenceRequest(typeof(SyncItem), 3))は単に弱い型付けされている解決:より宣言バージョンにリファクタリングするための努力において

カスタマイズされたバージョン

は、あなたが最初a Customizationでのスクランブルアルゴリズムをカプセル化することができます非一般的な)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は、fixturesutインスタンスを作成するときにも常に同じ配列が再利用されることを保証します。

条約ベースのテスト

上記試験は[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 Frameworkが属性の順序を保証しないことです。

代わりに、あなたはこのように、適用する単一の属性を定義することができます。

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]属性をCustomiなしで使用していますこれはスクランブリングがパラメータレベルで[UnorderedFrozen]属性によって適用されるためです。

これは、[AutoData]-derived属性でカプセル化された(他の)テストスイート全体の規約を使用して、オプトインメカニズムとして[UnorderedFrozen]を使用することも可能にします。

+2

うわー、マーク、私はこの答えへの投資に感謝します。この進歩は、自動フィニッシュのレイヤーを理解する上で本当に貴重なものであり、どのように私が対処するシナリオのそれぞれの種類の中で私が欲しいものを実際に得るためにそれらを剥がし始めることができるのですか?名誉とありがとう! – cocogorilla

関連する問題