Wie schreibt man Unit-Testfälle?

13

Manchmal schreibe ich Unit-Testfälle für Code, den andere Entwickler geschrieben haben. Es gibt Fälle, in denen ich wirklich nicht weiß, was der Entwickler versucht (der geschäftliche Teil) und ich manipuliere einfach den Testfall, um die grüne Linie zu erhalten. Sind diese Dinge in der Branche normal?

Was ist der normale Trend? Sollen die Entwickler Unit-Testfälle für Code schreiben, den sie selbst geschrieben haben?

Vinoth Kumar CM
quelle
2
"dint"? Was bedeutet "dint"?
S.Lott

Antworten:

12

Versuchen Sie, diesen Blog-Beitrag zu lesen: Schreiben von großartigen Unit-Tests: Beste und schlechteste Praktiken .

Aber es gibt unzählige andere im Internet.

In direkter Antwort auf Ihre Fragen ...

  1. "Normaler Trend" - Ich denke, das kann von Ort zu Ort unterschiedlich sein, was für mich normal ist, ist für andere vielleicht merkwürdig.
  2. Ich würde sagen (nach meiner Wahl), dass der Entwickler, der den Code schreibt, den Test schreiben sollte, idealerweise mit Methoden wie TDD, bei denen Sie den Test vor dem Code schreiben würden. Aber andere mögen hier andere Methoden und Ideen haben!

Und die Art und Weise, wie Sie das Schreiben der Tests (in Ihrer Frage) beschrieben haben, ist völlig falsch !!


quelle
9

Dieser Ansatz macht den Komponententest wertlos.

Der Komponententest muss fehlschlagen, wenn eine echte Aktion nicht wie beabsichtigt funktioniert. Wenn Sie das nicht so machen und vielleicht sogar den Test vor dem zu testenden Code schreiben, ist das so, als hätten Sie einen nicht funktionierenden Rauchmelder.


quelle
8
Das ist nicht ganz richtig. Oder besser gesagt, es ist wahr in einer idealen Welt, aber leider sind wir oft weit davon entfernt. Erwägen Sie, Legacy-Code ohne Tests und ohne Spezifikationen zu haben, und ohne jemanden, der Ihnen zuverlässig bis ins kleinste Detail sagen kann, was ein bestimmter Code genau tun soll (dies ist in einem großen Teil der vorhandenen Projekte Realität). Auch in diesem Fall kann es sich noch lohnen, Unit-Tests zu schreiben, um den aktuellen Status des Codes zu sperren und um sicherzustellen, dass Sie bei zukünftigen Umgestaltungen, Fehlerkorrekturen oder Erweiterungen nichts kaputt machen.
Péter Török
2
Außerdem meintest du wohl "schreibe den Test nach dem zu testenden Code", oder?
Péter Török
@ Péter, die Formulierung ist falsch - du hast es richtig verstanden. Wenn Sie sich jedoch entschließen, Tests zu schreiben, sollten sie etwas tun , um nützlich zu sein. Nur blind Code aufzurufen, der sagt, es sei ein Test, ist meiner Meinung nach kein Test.
ørn, wenn Sie meinen, dass wir in unseren Komponententests aussagekräftige Aussagen haben müssen, um zu überprüfen, ob der getestete Code tatsächlich das tut, was wir glauben , dass er es tut, stimme ich voll und ganz zu.
Péter Török
3

Wenn Sie nicht wissen, was eine Funktion tut, können Sie keinen Komponententest dafür schreiben. Soweit Sie wissen, macht es nicht einmal das, was es soll. Sie müssen zuerst herausfinden, was es tun soll. Dann schreiben Sie den Test.

Edward Strange
quelle
3

In der realen Welt ist es völlig normal, Komponententests für den Code eines anderen zu schreiben. Sicher, der ursprüngliche Entwickler hätte dies bereits tun sollen, aber häufig erhalten Sie einen Legacy-Code, in dem dies einfach nicht getan wurde. Übrigens spielt es keine Rolle, ob dieser Legacy-Code vor Jahrzehnten aus einer weit entfernten Galaxie stammt oder ob einer Ihrer Mitarbeiter ihn letzte Woche eingecheckt hat oder ob Sie ihn heute geschrieben haben. Legacy-Code ist Code ohne Tests

Fragen Sie sich: Warum schreiben wir Komponententests? Going Green ist offensichtlich nur ein Mittel zum Zweck. Das ultimative Ziel ist es, Aussagen über den getesteten Code zu beweisen oder zu widerlegen.

Angenommen, Sie haben eine Methode, die die Quadratwurzel einer Gleitkommazahl berechnet. In Java würde die Schnittstelle Folgendes definieren:

public double squareRoot(double number);

Es spielt keine Rolle, ob Sie die Implementierung geschrieben haben oder ob es jemand anderes getan hat, Sie möchten ein paar Eigenschaften von squareRoot behaupten:

  1. dass es einfache Wurzeln wie sqrt (4.0) zurückgeben kann
  2. dass es eine echte Wurzel wie sqrt (2.0) mit einer vernünftigen Genauigkeit finden kann
  3. dass es herausfindet, dass sqrt (0.0) 0.0 ist
  4. dass es eine IllegalArgumentException auslöst, wenn eine negative Zahl eingegeben wird, dh auf sqrt (-1.0)

Sie schreiben diese also als Einzeltests:

@Test
public void canFindSimpleRoot() {
  assertEquals(2, squareRoot(4), epsilon);
}

Hoppla, dieser Test schlägt bereits fehl:

java.lang.AssertionError: Use assertEquals(expected, actual, delta) to compare floating-point numbers

Sie haben die Gleitkomma-Arithmetik vergessen. OK, du stellst vor double epsilon=0.01und gehst:

@Test
public void canFindSimpleRootToEpsilonPrecision() {
  assertEquals(2, squareRoot(4), epsilon);
}

und füge die anderen Tests hinzu: endlich

@Test
@ExpectedException(IllegalArgumentException.class)
public void throwsExceptionOnNegativeInput() {
  assertEquals(-1, squareRoot(-1), epsilon);
}

und hoppla nochmal:

java.lang.AssertionError: expected:<-1.0> but was:<NaN>

Sie sollten getestet haben:

@Test
public void returnsNaNOnNegativeInput() {
  assertEquals(Double.NaN, squareRoot(-1), epsilon);
}

Was haben wir hier gemacht? Wir haben mit ein paar Annahmen über das Verhalten der Methode begonnen und festgestellt, dass nicht alle zutreffen. Wir haben dann die Testsuite Green erstellt, um den Nachweis zu erbringen, dass sich die Methode gemäß unseren korrigierten Annahmen verhält. Jetzt können sich Clients dieses Codes auf dieses Verhalten verlassen. Wenn jemand die tatsächliche Implementierung von squareRoot durch etwas anderes austauschen würde, was beispielsweise wirklich eine Ausnahme auslöste, anstatt NaN zurückzugeben, würden unsere Tests dies sofort feststellen.

Dieses Beispiel ist trivial, aber häufig erben Sie große Codeteile, bei denen nicht klar ist, was sie tatsächlich tun. In diesem Fall ist es normal, einen Testgurt um den Code zu legen. Beginnen Sie mit ein paar grundlegenden Annahmen darüber, wie sich der Code verhalten soll, schreiben Sie Unit-Tests für sie, testen Sie. Wenn Grün, gut, schreibe mehr Tests. Wenn Rot, haben Sie jetzt eine fehlgeschlagene Behauptung, die Sie gegen eine Spezifikation halten können. Vielleicht liegt ein Fehler im Legacy-Code vor. Vielleicht ist die Spezifikation über diesen bestimmten Eingang unklar. Vielleicht haben Sie keine Spezifikation. In diesem Fall schreiben Sie den Test so, dass das unerwartete Verhalten dokumentiert wird:

@Test
public void throwsNoExceptionOnNegativeInput() {
  assertNotNull(squareRoot(-1)); // Shouldn't this fail?
}

Mit der Zeit erhalten Sie ein Testkabel, das das tatsächliche Verhalten des Codes dokumentiert und zu einer Art codierter Spezifikation wird. Wenn Sie den alten Code jemals ändern oder durch einen anderen Code ersetzen möchten, können Sie mithilfe des Testkabels überprüfen, ob sich der neue Code gleich oder anders verhält als erwartet und kontrolliert (z. B. tatsächlich) behebt den Fehler, von dem Sie erwarten, dass er behoben wird). Dieses Gurtzeug muss am ersten Tag nicht komplett sein. Tatsächlich ist es fast immer besser, ein unvollständiges Gurtzeug zu haben, als überhaupt kein Gurtzeug zu haben. Ein Gurtzeug zu haben bedeutet, dass Sie Ihren Client-Code einfacher schreiben können, dass Sie wissen, wo die Dinge brechen, wenn Sie etwas ändern, und wo sie kaputt sind, als sie es schließlich taten.

Sie sollten versuchen, aus der Einstellung herauszukommen, dass Sie Komponententests schreiben müssen, nur weil Sie müssen, als würden Sie Pflichtfelder in einem Formular ausfüllen. Und Sie sollten keine Unit-Tests schreiben, nur um die rote Linie grün zu machen. Unit-Tests sind nicht deine Feinde, Unit-Tests sind deine Freunde.

Wallenborn
quelle
1

Wenn ich Testfälle schreibe (für Drucker), versuche ich an die einzelnen kleinen Komponenten zu denken ... und was kann ich tun, um sie möglicherweise zu zerstören? Sagen wir, der Scanner zum Beispiel, welche Befehle er verwendet (in der PJL-Druckersprache), was kann ich schreiben, um alle Funktionen zu testen?

Ich versuche das für jede Hauptkomponente zu tun, aber wenn es um Software und nicht so viel Hardware geht, möchte man sich jede Methode / Funktion ansehen und Grenzen und so weiter prüfen.


quelle
1

Es hört sich so an, als würden Sie mit anderen Entwicklern zusammenarbeiten (oder von anderen Entwicklern geschriebenen Code warten), die keine Komponententests durchführen. In diesem Fall sollten Sie auf jeden Fall wissen, was das zu testende Objekt oder die zu testende Methode bewirken soll, und dann einen Test dafür erstellen.

Es wird kein TDD sein, weil Sie den Test nicht zuerst geschrieben haben, aber Sie könnten die Situation verbessern. Möglicherweise möchten Sie auch eine Kopie der zu testenden Objekte mit Stubs erstellen, um sicherzustellen, dass Ihre Tests ordnungsgemäß funktionieren, wenn der Code fehlschlägt.

vjones
quelle