Unit Testing - Erste Schritte


14

Ich fange gerade erst mit Unit-Tests an, bin mir aber nicht sicher, ob ich den Sinn des Ganzen wirklich verstehe. Ich lese Tutorials und Bücher darüber, habe aber nur zwei kurze Fragen:

  1. Ich dachte, der Zweck des Unit-Tests ist es, den Code zu testen, den wir tatsächlich geschrieben haben. Mir scheint jedoch, dass wir, um den Test ausführen zu können, den Originalcode ändern müssen. In diesem Moment testen wir nicht den Code, den wir geschrieben haben, sondern den Code, den wir zum Testen geschrieben haben.

  2. Die meisten unserer Codes basieren auf externen Quellen. Bei der Überarbeitung unseres Codes, selbst wenn er den ursprünglichen Code verletzen würde, würden unsere Tests weiterhin einwandfrei funktionieren, da die externen Quellen nur Fehler in unseren Testfällen darstellen. Misslingt es nicht den Zweck des Komponententests?

Es tut mir leid, wenn ich hier dumm klinge, aber ich dachte, jemand könnte mich ein bisschen aufklären.

Danke im Voraus.

Antworten:


7

Meine 0,02 $ ... das ist ein bisschen subjektiv, also nimm ein Körnchen Salz mit, aber hoffentlich bringt es dich zum Nachdenken und / oder löst einen Dialog aus:

  1. Der Hauptzweck von Unit-Tests besteht für mich darin, sicherzustellen, dass der von Ihnen geschriebene Code die Verträge und Randfälle erfüllt, die Ihr Code erfüllen soll. Mit den vorhandenen Komponententests können Sie besser sicherstellen, dass, wenn Sie (oder in Zukunft jemand anderes) Ihren Code überarbeiten, alle externen Konsumenten Ihres Codes davon unberührt bleiben, wenn Sie über eine ordnungsgemäße staatliche Abdeckung verfügen. (Zumindest in dem Maße, in dem Sie beabsichtigen, dass sie nicht betroffen sind).

    In den meisten Fällen sollten Sie in der Lage sein, Code zu schreiben, der sowohl in die Produktion geliefert werden kann als auch auf einfache Weise für die Prüfung von Einheiten geeignet ist. Ein guter Anfang kann sein, sich mit Abhängigkeitsinjektionsmustern und -frameworks zu befassen. (Oder andere Philosophien für Ihre Sprache / Plattform der Wahl).

  2. Es ist richtig, dass sich externe Implementierungen auf Ihren Code auswirken können. Es ist jedoch eine Funktion des Integrationstests, sicherzustellen, dass Ihr Code als Teil eines größeren Systems ordnungsgemäß funktioniert . (Was auch mit unterschiedlichem Aufwand automatisiert werden könnte).

    Im Idealfall sollte sich Ihr Code nur auf die API-Verträge von Drittanbieter-Komponenten stützen. Dies bedeutet, dass Ihre Unit-Tests immer noch einen Wert liefern, solange Ihre Mocks die richtige API erfüllen.

    Das heißt, ich gebe gerne zu, dass ich manchmal auf Unit-Tests zugunsten von Integrationstests verzichtet habe, aber dies waren nur Fälle, in denen mein Code so stark mit Komponenten von Drittanbietern mit schlecht dokumentierten APIs interagieren musste. (dh eher die Ausnahme als die Regel).


5
  1. Versuchen Sie zuerst, Ihre Tests zu schreiben. Auf diese Weise haben Sie eine solide Grundlage für das Verhalten Ihres Codes und Ihr Test wird zum Vertrag für das erforderliche Verhalten Ihres Codes. Das Ändern des Codes zum Bestehen des Tests wird zu "Ändern des Codes zum Erfüllen des vom Test vorgeschlagenen Vertrags", anstatt "Ändern des Codes zum Bestehen des Tests".
  2. Achten Sie auf den Unterschied zwischen Stubs und Mocks. Von Änderungen im Code unberührt zu bleiben, ist ein charakteristisches Verhalten von Stubs, jedoch keine Verspottung. Beginnen wir mit der Definition des Mocks:

    Ein Mock-Objekt ersetzt ein reales Objekt unter Testbedingungen und ermöglicht die Überprüfung der Aufrufe (Interaktionen) gegen sich selbst als Teil eines System- oder Komponententests.

    -Die Kunst des Unit-Testens

Grundsätzlich sollten Ihre Mocks das erforderliche Verhalten Ihrer Interaktionen überprüfen. Wenn Ihre Interaktion mit der Datenbank nach einem Refactoring fehlschlägt, sollte Ihr Test mit dem Mock ebenfalls fehlschlagen. Dies hat natürlich Einschränkungen, aber bei sorgfältiger Planung können Ihre Mocks viel mehr als nur "da sitzen" und "den Zweck des Komponententests nicht zunichte machen".


1

Eine gute Frage zu stellen ist in keiner Weise dumm.

Ich werde Ihre Fragen beantworten.

  1. Der Zweck des Unit-Tests besteht nicht darin, den Code zu testen, den Sie bereits geschrieben haben . Es hat keine Ahnung von Zeit. Nur in TDD solltest du zuerst testen, aber das gilt strikt nicht für irgendwelche Unit-Tests. Der Punkt ist, in der Lage zu sein, Ihr Programm auf Klassenebene automatisch und effizient zu testen. Sie tun, was Sie tun müssen, um dorthin zu gelangen, auch wenn dies eine Änderung des Codes bedeutet. Und lassen Sie mich Ihnen ein Geheimnis verraten - das bedeutet es oft.
  2. Wenn Sie einen Test schreiben, haben Sie zwei Hauptoptionen, um sicherzustellen, dass Ihr Test korrekt ist:

    • Variieren Sie die Eingaben für jeden Test
    • Schreiben Sie einen Testfall, der sicherstellt, dass Ihr Programm ordnungsgemäß funktioniert, und schreiben Sie dann einen entsprechenden Testfall, der sicherstellt, dass Ihr Programm nicht so funktioniert, wie es sollte

    Hier ist ein Beispiel:

    TEST(MyTest, TwoPlusTwoIsFour) {
        ASSERT_EQ(4, 2+2);
    }
    
    TEST(MyTest, TwoPlusThreeIsntFour) {
        ASSERT_NE(4, 2+3);
    }
    

    Wenn Sie die Logik in Ihrem Code (nicht in den Bibliotheken von Drittanbietern) auf Unit-Tests testen , ist es vollkommen in Ordnung, dass Sie sich in diesem Kontext keine Sorgen darüber machen, dass der andere Code kaputt geht. Sie testen im Wesentlichen die Art und Weise, in der Ihre Logik umgebrochen wird, und verwenden die Dienstprogramme von Drittanbietern. Dies ist ein klassisches Testszenario.

Sobald Sie mit dem Testen auf Klassenebene fertig sind, können Sie die Integration zwischen Ihren Klassen (den Mediatoren, soweit ich weiß) und den Bibliotheken von Drittanbietern testen. Diese Tests werden Integrationstests genannt und verwenden keine Mocks, sondern die konkreten Implementierungen aller Klassen. Sie sind etwas langsamer, aber trotzdem sehr wichtig!


1

Es hört sich so an, als hätten Sie eine monolithische App, die void main()vom Datenbankzugriff bis zur Ausgabegenerierung alles erledigt . Es gibt hier mehrere Schritte, bevor Sie mit dem ordnungsgemäßen Testen der Einheit beginnen können.

1) Suchen Sie einen Code, der mehr als einmal geschrieben / kopiert wurde. Auch wenn es nur so ist string fullName = firstName + " " + lastName. Teilen Sie das in eine Methode auf, wie:

private static string GetFullName (firstName, lastName)
{
    return firstName + " " + lastName;
}

Jetzt haben Sie einen Code, dessen Einheit getestet werden kann, so trivial er auch sein mag. Schreiben Sie einen Komponententest dafür. Spülen und wiederholen Sie diesen Vorgang. Letztendlich werden Sie eine Menge logisch gruppierter Methoden haben und Sie können eine Reihe von Klassen daraus extrahieren. Die meisten dieser Klassen können getestet werden.

Als Bonus können Sie, sobald Sie mehrere Klassen extrahiert haben, Schnittstellen aus diesen extrahieren und Ihr Programm aktualisieren, um mit Schnittstellen anstelle der Objekte selbst zu kommunizieren. Zu diesem Zeitpunkt können Sie ein Mocking / Stubbing-Framework (oder sogar Ihre eigenen handgerollten Fakes) verwenden, ohne das Programm zu ändern. Dies ist sehr praktisch, wenn Sie die Datenbankabfragen in eine Klasse (oder in mehrere) extrahiert haben, da Sie jetzt die Daten fälschen können, die die Abfrage zurückgeben soll .


0

Ein kluger Typ sagte: "Wenn es schwer zu testen ist, ist es wahrscheinlich schlechter Code." Deshalb ist es keine schlechte Sache, Code neu zu schreiben, um ihn unittest zu können. Code mit externen Abhängigkeiten ist SEHR SCHWER zu testen, stellt ein Risiko für den Code dar und sollte nach Möglichkeit vermieden und auf integrationsspezifische Bereiche Ihres Codes konzentriert werden, fx. Fassaden- / Gateway-Typklassen. Dadurch wird eine Änderung der externen Komponente viel einfacher zu bewältigen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.