Autofixture: NSubstitute.ReceivedCalls() gibt mit AutoFixture.AutoNSubstitute einen falschen Wert zurück

Erstellt am 28. März 2018  ·  10Kommentare  ·  Quelle: AutoFixture/AutoFixture

Wir haben viele Tests mit 'Class.ReceivedCalls()' und 'var tmp = Class.Received(int).Property;' um die Anrufanzahl zu überprüfen. In v3 von AutoFixture hat es die Anrufanzahl korrekt gemeldet, insbesondere von Eigenschaften, was es nicht mehr tut. Die Aufrufanzahl der Methoden scheint noch in Ordnung zu sein.
Vorausgesetzt, wir haben den folgenden Code:

    public interface IRunSpeed
    {
        int Speed { get; }
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public void DoItNow()
        {
            var runningSpeed = _runSpeed.Speed;
        }
    }

Und da wir die folgenden Tests haben:

        [Fact]
        public void DoItNow_WithOutAutoNSubstitute()
        {
            // Arrange
            var runSpeed = Substitute.For<IRunSpeed>();
            runSpeed.Speed.Returns(2);

            var sut = new GetToDaChoppa(runSpeed);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());
        }

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());

Mein AutoNSubstituteDataAttribute sieht so aus:

        public class AutoNSubstituteDataAttribute : AutoDataAttribute
        {
            public AutoNSubstituteDataAttribute()
                : base(() => new Fixture()
                           .Customize(new AutoNSubstituteCustomization()))
            {
            }
        }

Der erste Test 'DoItNow_WithOutAutoNSubstitute' funktioniert einwandfrei. Aber der zweite Test 'DoItNow_UsingAutoNSubstitute' gibt 2 für 'ios.Received(1).Speed;' zurück.
Es gibt auch 2 für 'runSpeed.ReceivedCalls();' zurück.
Aus diesem Grund können wir unsere Lösungen derzeit nicht auf v4 aktualisieren, da wir sofort >1000 fehlgeschlagene Tests pro Lösung haben. Gibt es eine Anleitung, was das Problem sein könnte oder wo man nach der Lösung suchen kann?

question

Alle 10 Kommentare

Danke, dass du das Problem ausgelöst hast. Eigentlich hat dieses Problem nichts mit AutoFixture zu tun, obwohl wir auch davon betroffen sind 😕

Dieses Problem wird hauptsächlich durch xUnit verursacht und wenn Sie den Test wie folgt neu schreiben, wird er bestanden:
```c#
[Theorie, AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute_CreateManually(IFixture Fixture)
{
// Arrangieren
var runSpeed ​​= Fixture.Freeze();
var sut = Fixture.Create();
runSpeed.Speed.Returns(49);

//Act
sut.DoItNow();

//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Single(runSpeed.ReceivedCalls());

}
```

Wenn Sie Tests ausführen, versucht xUnit, den Testnamen für Sie zu formatieren. Wenn es feststellt, dass der Typ nicht bekannt ist und keine ToString() Überladung vorliegt, verwendet es die strukturelle Inspektion und ruft die Eigenschaftswerte rekursiv ab. Wenn Sie den Testnamen überprüfen, sehen Sie Folgendes:
image

Wie Sie vielleicht bemerken, hat xUnit den Eigenschaftswert Speed abgerufen, um Ihnen das Testargument gut zu zeigen. Da dies ein üblicher Aufruf ist, hat NSubstitute dies als Aufruf gezählt.

Es ist ziemlich seltsam, dass Sie dieses Problem mit v3 nicht hatten, da sich diesbezüglich nichts geändert hat. Ich habe gerade xUnit2 + AutoFixture v3 getestet und habe immer noch das Problem. Wahrscheinlich haben Sie auch die xUnit aktualisiert.


Als Workaround habe ich gute und schlechte Nachrichten. Die gute Nachricht ist, dass dies in der nächsten Hauptversion von NSubsitute behoben wird, da die Methode ToString() überschrieben wurde, um eine Proxy-ID zurückzugeben. Als Ergebnis berührt xUnit die Eigenschaften nicht mehr:
image

Die schlechte Nachricht ist, dass NSubsitute v4 noch nicht veröffentlicht wurde.

Ich könnte dir entweder vorschlagen:

  • Verschieben Sie die Migration, bis NSubsitute v4 veröffentlicht wird;
  • Verwenden Sie den neuesten master Quellcode von NSubsitute, erstellen Sie einen lokalen Build und verwenden Sie Ihr eigenes NSubsitute.dll anstelle des NuGet-Pakets. Nach der Veröffentlichung von v4 können Sie zum NuGet-Paket wechseln.
  • Wenn Sie auch die xUnit-Version aktualisiert haben (dies könnte erklären, warum dieses Problem zuvor nicht aufgetreten ist), machen Sie diese Änderung rückgängig und verwenden Sie die vorherige Version von xUnit.

Ich entschuldige mich für die Unannehmlichkeiten, aber leider können wir auf der AutoFixture-Seite nichts tun, um dieses Problem zu beheben.

habe das gerade getestet. Schließlich haben unsere Tests, die ReceivedCalls bestätigen, wieder ein erfolgreiches Ergebnis erzielt.
Etwas, das immer noch fehlschlägt, sind die Asserts auf Received(x) wie Received(1).Speed ​​Daniel, die oben erwähnt wurden.

snip_20180328181559

Habt ihr auch eine Antwort/Lösung für Hut?

Arbeitet ihr am selben Projekt? :) Wenn ja, könnten Sie klarstellen, wie Sie das Problem behoben haben?

Die oben vorgeschlagenen Optionen sollten bei beiden Problemen helfen. Wenn sie dies nicht tun, klären Sie das Szenario.

nicht wirklich das gleiche Projekt, sondern bei der gleichen Firma.
wir sind schon lange auf xunit 1.9 + nsubstitute 2 / 3 und haben es endlich geschafft, unsere internen Assemblies (Nuget-Pakete) zu bekommen, also ist ein einfacher Weg zurück zu einer älteren Version von nur einer Referenz nicht so einfach, wie es sich anhört sein) kompatibel zu xunit2 sein. und jetzt haben wir diese beiden probleme.

Wir haben schon einiges ausprobiert und dachten schließlich, dass es etwas mit Autofixture zu tun haben muss - wegen des Frozen-Attributs und einer hartcodierten Testausführungszählung - oder so ähnlich.
nachdem daniel diesen fall eröffnet hat, hat er mir den link dazu gepostet.

genau wie Sie oben beschrieben haben, habe ich die Nuget 3.1-Referenz durch die aus dem frisch kompilierten nsubstitute-Projekt ersetzt. danach habe ich jetzt 118 statt 178 fehlgeschlagene Tests, weil Receiced(x) immer noch die falsche Anzahl von Anrufen auswertet.

mein Projekt basiert derzeit auf .net 4.5.2 mit xunit2, aktueller Repo-Version von nsubstitute und referenziert auf Autofixture 4.2. alle Tests schlagen aus den gleichen zwei Gründen fehl - außerdem gibt es immer noch einige empfangene Anrufe, die in den Komponententests fehlschlagen.
Ich glaube, ich muss mir das nochmal genauer anschauen, wenn ich wieder im Büro bin (Ostern unterwegs).
vielleicht kann @dklinger mittlerweile das Szenario beschreiben.

@dklinger @evilbaschdi Bitte installiert haben.

Öffnen Sie das Problem erneut, um anzugeben, dass die Ermittlungen noch andauern.

Ok, also ich habe die letzten Stunden getestet. Erstens: Danke für deine Antwort, deine Erklärung und die Vorschläge - das hat sehr geholfen.
Ich habe v4 von NSubstitute getestet. Es funktioniert für meinen obigen Beispielcode, löst das Problem jedoch nicht vollständig in unseren realen Projekten. Das Problem ist, dass es das Problem der falschen ReceivedCalls-Zählung nur für SUT-Aufrufe an Methoden, nicht an Eigenschaften, behebt.

Beispiel für unseren jetzt funktionierenden Code:

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Aber wenn ich die Methode "GetToDaChoppa.DoItNow();" ändere um eine Eigenschaft "GetToDaChoppa.DoItNow" zu sein, ist ReceivedCallsCount wieder +1:

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Ich denke, es hat wieder mit der xUnit-Namensgebung zu tun, da wir diese für die Arbeitsmethode-Implementierung bekommen:
image

Und das für die nicht funktionierende Property-Implementierung:
image

Es sieht so aus, als ob xUnit die SUT-Eigenschaft aufruft, um ihren Wert abzurufen, um ihn für den Testnamen zu verwenden, aber dies tut es nicht für SUT-Methoden. Mein erster Gedanke war, dass es mit dem Rückgabewert unserer Methode "GetToDaChoppa()" zu tun hat, die void war. Aber selbst nachdem es in "public int GetToDaChoppa()" geändert wurde, ruft xUnit es immer noch nicht auf, um einen Rückgabewert für den Testnamen zu erhalten. Es ist nur ein Problem mit Eigenschaften.

Im Moment stecke ich wieder fest. Ich stimme voll und ganz zu, dass dies kein Problem von AutoFixture ist. Aber wenn Sie alle Pakete besser kennen als wir, was würden Sie Ihrer Meinung nach vorschlagen?

  • eine Ausgabe für NSubstitute eröffnen?
  • ein Problem für xUnit2 eröffnen?
  • oder etwas ganz neues?

@dklinger Danke fürs Nachfassen! Könnten Sie bitte den MCVE- Code für das letzte Szenario, das immer noch fehlschlägt, teilen? Nur damit nichts übersehen wird und ich die Bedingungen nicht falsch verstanden habe.

Danach werde ich versuchen zu untersuchen, warum das passiert und wie man das umgehen kann.

Vielen Dank.

Ja, sicher. Hier ist es:

System im Test:

    public interface IRunSpeed
    {
        int Speed { get; }
        void Dude();
        int Dude2();
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public int DoItNow
        {
            get
            {
                var runningSpeed = _runSpeed.Speed;
                return 0;
            }
        }
    }

Testfall:

        [Theory, AutoNSubstituteData]
        public void DoItNowAsProperty_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Aktuell versuche ich die Diskussionen unter https://github.com/xunit/xunit/issues/1386 und https://github.com/AutoFixture/AutoFixture/issues/805 nachzuholen. Vielleicht machen wir etwas falsch oder es gibt eine Möglichkeit, xUnit anzuweisen, den Testnamen nicht automatisch zu generieren.
Ich habe auch versucht, mein eigenes TheoryAttribute zu erstellen, das die DisplayName-Eigenschaft überschreibt, aber das hilft auch nicht, da xUnit in gewisser Weise immer noch den automatisch generierten Namen intern verwendet. Aber das ist nur zu Ihrer Information und hat nichts mit dem obigen Demo-Code zu tun.

        public sealed class MyTheoryAttribute : TheoryAttribute
        {
            public MyTheoryAttribute([CallerMemberName] string memberName = null)
            {
                DisplayName = "MyTestCase";
            }
        }

image

Hallo, ich nochmal :)
Es ist tatsächlich xUnit2, das alle Eigenschaften und Felder von Testmethodenparametern aufruft, die von einem komplexen Typ sind. Die Codezeile ist diese: https://github.com/xunit/assert.xunit/blob/2b70a9b0c5bb291f98472ec24cec437acf8d65c8/Sdk/ArgumentFormatter.cs#L156

Danke für deine Unterstützung. Ich habe ein Problem im xUnit-Repo wieder geöffnet, wo die Diskussion weitergehen sollte: https://github.com/xunit/xunit/issues/1682

@dklinger Danke für das ausführliche Szenario. Es ist in der Tat ziemlich knifflig und es ist schwer, es irgendwie zu umgehen. Aus AutoFixture- und NSubsitute-Sicht gibt es keinen Unterschied, ob Code irgendwo tief in xUnit oder im Testkörper aufgerufen wird.

Normalerweise können Sie als Workaround die klare Funktion von NSubstitute verwenden:

runSpeed.ClearReceivedCalls();

Dieser Code sollte beim Prolog jedes Tests ausgeführt werden, bei dem Sie die genaue Anzahl der Anrufe überprüfen. Es funktioniert gut, wenn Sie ein paar Tests haben, aber wenn Tausende von Tests betroffen sind, wird es natürlich nicht viel helfen 😅

Es ist schade, dass die Integration von NSubsitute + xUnit2 + AutoFixture nicht gut funktioniert und unter solchen Problemen leidet. AutoFixture-Produkt wurde entwickelt, um das Leben zu vereinfachen, anstatt es zu einem Albtraum zu machen 😕 Ich hoffe, die Jungs von xUnit werden Ihnen einen schnellen Weg zur Lösung des Problems für das gesamte Projekt geben.

Lassen Sie es mich wissen, wenn Sie glauben, dass wir von unserer Seite aus etwas tun können, um die Situation zu verbessern.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

ploeh picture ploeh  ·  7Kommentare

zvirja picture zvirja  ·  4Kommentare

ecampidoglio picture ecampidoglio  ·  7Kommentare

zvirja picture zvirja  ·  3Kommentare

JoshKeegan picture JoshKeegan  ·  6Kommentare