2017-11-26 6 views
2

Mit AutoFixture 3.50 und xUnit.NET scheint es, dass es einen Unterschied zwischen der Art und Weise, wie AutoData-Theorie Tests konkrete Objekte erstellen, Fixture.Create() schafft konkrete Objekte erstellt.AutoDataAttribute die konkrete Objekt Erstellungslogik ruft alle Eigenschaft Getter einmal

Einfaches Beispiel:

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

-Test Fixture mit:

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

Test AutoDataAttribute:

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

Im ersten Test wird der Breakpoint 'B1' getroffen, während der Breakpoint 'A' niemals getroffen wird. Im letzteren Test wird der Haltepunkt 'A' getroffen, bevor der Haltepunkt 'B2' getroffen wird. Dies ist problematisch, wenn ich die Eigenschaft "träge" initialisiert habe, die der obigen nicht unähnlich ist - weil das Hintergrundfeld der Eigenschaft initialisiert wird, bevor der Test ausgeführt wird, kann ich die Initialisierungslogik nicht testen.

Gibt es eine Möglichkeit, AutoDataAttribute anzupassen, so dass ich dieses Verhalten umgehen kann? Oder vielleicht, vielleicht ist das ein Fehler?

Antwort

4

Dies ist in der Tat kein AutoFixture-Problem, sondern ein xUnit.net-Problem, wenn Sie so wollen. Sie können es ohne AutoFixture wie dies vollständig reproduzieren:

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

Wenn Sie diese ClassDataTest debuggen in, werden Sie auch Haltepunkt ‚A‘, bevor Sie Breakpoint ‚B3‘ getroffen.

Der Grund dafür ist, dass der xUnit.net Test Runner Ihnen für jeden parametrisierten Test einen guten Anzeigenamen geben möchte. Daher versucht er, eine lesbare Stringdarstellung aller Argumente zu erstellen, die an jeden Testfall in einer [Theory] übergeben werden.

Wenn ein Datenobjekt ToString nicht explizit überschreibt, greift xUnit.net auf alle Eigenschaften zurück und erstellt daraus eine Anzeigezeichenfolge. Das passiert hier.

Ich habe versucht, nach einer offiziellen Dokumentation dieses Verhaltens zu suchen, aber das Beste, was ich finden konnte, ist this. Da es vorgeschlagen, können Sie durch zwingende ToString rund um das Thema zu bekommen:

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"; 
    } 
} 

Diese Änderung Breakpoint ‚A‘ aus wird getroffen stoppt, sowohl wenn ClassData und AutoData verwenden. Ob Sie die ToString überschreiben möchten, ist eine andere Frage.

Wenn Sie nicht ToString auf Foo außer Kraft setzen möchten, können Sie vielleicht, um das Problem zu erhalten, indem ein Testspezifische Kind-Klasse zu testen, die ToString, wie dies enthebt:

[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"; 
    } 
} 

Dies hält auch Unterbrechungs 'A' von getroffen werden.

Solange Foo nicht sealed ist, sollten Sie dies tun können.Wenn Foosealed ist, kann ich an keine andere Problemumgehung denken, als den Test im Stil von FixtureTest zu schreiben.

+0

Danke für die detaillierte Antwort! – MrPiao

Verwandte Themen