2017-11-26 20 views
2

AutoFixture 3.50とxUnit.NETを使用すると、Fixture.Create()が具体的なオブジェクトを作成する方法とAutoData Theoryテストが具体的なオブジェクトを作成する方法に違いがあるようです。AutoDataAttributeの具体的なオブジェクト作成ロジックは、一度すべてのプロパティゲッターを呼び出します

簡単な例:Fixtureを用い

public class Foo 
{ 
    private string prop; 
    public string Prop 
    { 
     get 
     { 
      if (prop == null) { prop = "Prop"; } // Breakpoint 'A' 
      return prop; 
     } 
    } 
} 

をテスト:

[Fact] 
public void FixtureTest() 
{ 
    var fixture = new Fixture(); 
    var result = fixture.Create<Foo>(); // Breakpoint 'B1' 
} 

AutoDataAttributeを用いた試験:

[Theory, AutoData] 
public void AutoDataTest(Foo sut) 
{ 
    var bar = 1; // Essential no-op, Breakpoint 'B2' 
} 

前のテストでは、ブレークポイント 'B1'がヒットし、ブレークポイント 'A'は決してヒットしませんでした。後者のテストでは、ブレークポイント 'B2'がヒットする前にブレークポイント 'A'がヒットします。テストが実行される前にプロパティのバッキングフィールドが初期化されているため、初期化ロジックをテストできないため、これは上記のものと似ているわけではありません。

AutoDataAttributeをカスタマイズしてこの問題を回避する方法はありますか?あるいはおそらく、これはバグでしょうか?

答えて

4

これは、実際にはAuto Fixtureの問題ではなく、むしろxUnit.netの問題です。あなたは、このように完全にAutoFixtureせずにそれを再現することができます:あなたはこのClassDataTestにデバッグする場合

[Theory, ClassData(typeof(FooTestCases))] 
public void ClassDataTest(Foo sut) 
{ 
    var bar = 1; // Essential no-op, Breakpoint 'B3' 
} 

private class FooTestCases : IEnumerable<object[]> 
{ 
    public IEnumerator<object[]> GetEnumerator() 
    { 
     yield return new object[] { new Foo() }; 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

が、あなたはまた、「」あなたはブレークポイントにヒットする前に「B3」をブレークポイントにヒットします。

xUnit.netのテストランナーは、パラメータ化されたテストごとに適切な表示名を与えたいので、[Theory]に各テストケースに渡されたすべての引数の読み込み可能な文字列表現を作成しようとします。

データオブジェクトが明示的にToStringをオーバーライドしない場合、xUnit.netはすべてのプロパティの読み取りとそれらからの表示文字列の作成に戻ります。それがここで起こっていることです。

私はその動作の公式な文書を検索しようとしましたが、私が見つけた最高のものはthisです。そこに示唆したように、あなたがToStringをオーバーライドすることで問題を回避することができます

public class Foo 
{ 
    private string prop; 
    public string Prop 
    { 
     get 
     { 
      if (prop == null) { prop = "Prop"; } // Breakpoint 'A' 
      return prop; 
     } 
    } 

    public override string ToString() 
    { 
     return "Foo"; 
    } 
} 

をこの変更は、ヒットされてからブレークポイント「A」を停止し、両方ClassDataAutoDataを使用した場合。 ToStringを上書きするかどうかは別の質問です。

あなたはおそらく、あなたはこのように、ToStringを上書きしないテスト固有の子クラスをテストすることで問題を回避することができ、FooToStringを上書きしたくない場合:これはまた、ブレークポイントを停止

[Theory, AutoData] 
public void AutoDataTestFoo(TestFoo sut) 
{ 
    var bar = 1; // Essential no-op, Breakpoint 'B4' 
} 

public class TestFoo : Foo 
{ 
    public override string ToString() 
    { 
     return "Foo"; 
    } 
} 

'A'はヒットしない。

Foosealedでない限り、これを行うことができます。Foosealedの場合、私はFixtureTestのスタイルでテストを書く以外の方法は考えられません。

+0

詳細な回答ありがとうございます! – MrPiao

関連する問題