Müssen Tests für einfache (eigenständige) Funktionen durchgeführt werden?

36

Bedenken Sie:

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

Angenommen, Sie schreiben verschiedene Tests für die obige Funktion und beweisen sich und anderen, dass "es funktioniert".

Warum dann nicht diese Tests entfernen und glücklich für immer leben? Mein Punkt ist, dass einige Funktionen nicht kontinuierlich getestet werden müssen, nachdem nachgewiesen wurde, dass sie funktionieren. Ich suche Kontrapunkte, die besagen, ja diese Funktionen müssen noch getestet werden, weil: ... oder dass ja diese nicht getestet werden müssen ...

Dennis
quelle
32
Stimmt es nicht für jede Funktion, dass es auch morgen funktioniert, wenn Sie Ihren Code nicht ändern, wenn er gestern funktioniert hat? Der Punkt ist , dass die Software wird geändert.
5gon12eder
3
Denn niemand wird jemals override_function('pow', '$a,$b', 'return $a * $b;');bei klarem Verstand schreiben ... oder versuchen, es umzuschreiben, um mit komplexen Zahlen umzugehen.
8
Also ... ähm ... dieser "fehlerfreie Code" ... hat einen Fehler .
Für kleine Funktionen wie diese empfiehlt es sich, eigenschaftsbasierte Tests in Betracht zu ziehen. Eigenschaftsbasiertes Testen generiert automatisch Testfälle und testet auf vordefinierte Invarianten. Sie bieten Dokumentation über die Invarianten, wodurch sie nützlich sind, um sie zu behalten. Für einfache Funktionen wie diese sind sie perfekt.
Martijn
6
Auch diese Funktion ist ein guter Kandidat für eine Änderung von Horners Form, (($a*$x + $b)*$x + $c)*$x + $ddie leicht falsch ist, aber oft erheblich schneller ist. Nur weil du denkst, dass es sich nicht ändern wird, heißt das nicht, dass es sich nicht ändern wird.
Michael Anderson

Antworten:

78

Regressionstests

Es geht nur um Regressionstests .

Stellen Sie sich vor, der nächste Entwickler betrachtet Ihre Methode und merkt, dass Sie magische Zahlen verwenden. Ihm wurde gesagt, dass magische Zahlen böse sind, also erstellt er zwei Konstanten, eine für die Nummer zwei, die andere für die Nummer drei - es ist nichts Falsches daran, diese Änderung vorzunehmen; Es ist nicht so, als würde er Ihre bereits korrekte Implementierung modifizieren.

Abgelenkt kehrt er zwei Konstanten um.

Er schreibt den Code fest, und alles scheint gut zu funktionieren, da nach jedem Festschreiben keine Regressionstests ausgeführt werden.

Eines Tages (könnte Wochen später sein) bricht irgendwo etwas zusammen. Und anderswo meine ich den völlig entgegengesetzten Ort der Codebasis, der nichts mit polynominalFunktion zu tun zu haben scheint . Stunden schmerzhaftes Debuggen führen zum Täter. Während dieser Zeit fällt die Anwendung in der Produktion weiterhin aus, was Ihren Kunden viele Probleme bereitet.

Das Beibehalten der ursprünglichen Tests, die Sie geschrieben haben, kann solche Schmerzen verhindern. Der abgelenkte Entwickler gab den Code ein und stellte fast sofort fest, dass er etwas kaputt gemacht hatte. Ein solcher Code wird nicht einmal die Produktion erreichen. Unit-Tests werden außerdem den Ort des Fehlers sehr genau beschreiben . Es wäre nicht schwierig, es zu lösen.

Ein Nebeneffekt...

Tatsächlich basieren die meisten Umgestaltungen stark auf Regressionstests. Nehmen Sie eine kleine Änderung vor. Prüfung. Wenn es geht, ist alles in Ordnung.

Der Nebeneffekt ist, dass, wenn Sie keine Tests haben, praktisch jedes Refactoring ein großes Risiko darstellt, den Code zu brechen. Da dies in vielen Fällen der Fall ist, ist es bereits schwierig, dem Management zu erklären, dass das Refactoring durchgeführt werden sollte. Dies ist sogar noch schwieriger, nachdem bei Ihren vorherigen Refactoring-Versuchen mehrere Fehler aufgetreten sind.

Wenn Sie über eine vollständige Testsuite verfügen, fördern Sie das Refactoring und damit einen saubereren Code. Ohne Risiko wird es sehr verlockend, regelmäßig mehr zu refactern.

Änderungen der Anforderungen

Ein weiterer wesentlicher Aspekt ist, dass sich die Anforderungen ändern. Möglicherweise werden Sie aufgefordert , komplexe Zahlen zu verarbeiten , und plötzlich müssen Sie in Ihrem Versionskontrollprotokoll nach den vorherigen Tests suchen, diese wiederherstellen und neue Tests hinzufügen.

Warum all diese Probleme? Warum Tests entfernen, um sie später hinzuzufügen? Sie hätten sie an erster Stelle behalten können.

Arseni Mourzenko
quelle
Pedantischer Hinweis: Nichts ist risikofrei. ;)
jpmc26
2
Regression ist für mich der Hauptgrund für Tests - auch wenn Ihr Code klar über Verantwortlichkeiten ist und nur das verwendet, was an ihn weitergegeben wird (z. B. keine globalen Regeln), ist es allzu einfach, etwas aus Versehen zu brechen. Tests sind nicht perfekt, aber dennoch großartig (und verhindern fast immer, dass derselbe Fehler erneut auftritt, worüber die meisten Kunden ziemlich unglücklich sind). Wenn Ihre Tests funktionieren, fallen keine Wartungskosten an. Wenn sie nicht mehr funktionieren, haben Sie wahrscheinlich einen Fehler gefunden. Ich arbeite an einer Legacy-Anwendung mit Tausenden von Globals - ohne Tests würde ich nicht wagen, viele der notwendigen Änderungen vorzunehmen.
Luaan
Noch eine pedantische Anmerkung: Einige "magische" Zahlen sind tatsächlich in Ordnung, und es ist eine schlechte Idee, sie durch Konstanten zu ersetzen.
Deduplizierer
3
@Deduplicator Wir wissen das, aber viele besonders frisch ausgebildete Junior-Programmierer sind äußerst eifrig dabei, Mantras blind zu befolgen. Und "magische Zahlen sind böse" ist eine davon. Zum anderen muss "alles, was mehr als einmal verwendet wird, in eine Methode umgewandelt werden", was in extremen Fällen zu einer Vielzahl von Methoden führt, die nur eine einzige Anweisung enthalten (und sich dann fragen, warum die Leistung plötzlich abfällt und sie nie etwas über Callstacks erfahren haben).
Mittwoch,
@jwenting: Habe gerade diesen Hinweis hinzugefügt, weil MainMa geschrieben hat "es ist nichts Falsches daran, diese Änderung vorzunehmen".
Deduplikator
45

Denn nichts ist so einfach, dass es keine Bugs geben kann.

Ihr Code scheint auf den ersten Blick fehlerfrei zu sein. Es ist in der Tat eine einfache programmatische Darstellung einer Polynomfunktion.

Außer es hat einen Bug ...

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

$xist nicht als Eingabe für Ihren Code definiert. Abhängig von der Sprache, der Laufzeit oder dem Gültigkeitsbereich funktioniert Ihre Funktion möglicherweise nicht, führt möglicherweise zu ungültigen Ergebnissen oder stürzt ein Raumschiff ab .


Nachtrag:

Während Sie Ihren Code für jetzt für fehlerfrei halten können, ist es schwer zu sagen, wie lange das so bleibt. Man könnte argumentieren, dass es sich nicht lohnt, einen Test für ein so triviales Stück Code zu schreiben. Wenn Sie jedoch den Test bereits geschrieben haben, ist die Arbeit erledigt, und das Löschen dieses Tests löscht einen greifbaren Schutz.

Bemerkenswert sind auch Code-Coverage-Services (wie coveralls.io), die einen guten Hinweis auf die Coverage geben, die eine Testsuite bietet. Indem Sie jede Codezeile abdecken, geben Sie eine angemessene Metrik für die Quantität (wenn nicht die Qualität) der von Ihnen durchgeführten Tests an. In Kombination mit vielen kleinen Tests sagen Ihnen diese Dienste zumindest, wo Sie nicht nach einem Fehler suchen müssen, wenn er auftritt.

Wenn Sie bereits einen Test geschrieben haben, behalten Sie diesen . Weil die Zeit- oder Platzersparnis beim Löschen wahrscheinlich viel geringer ist als die technische Verschuldung, wenn später ein Fehler auftritt.


quelle
Es gibt eine alternative Lösung: Umstellung auf nicht dynamische, nicht schwache Sprachen. Komponententests sind in der Regel eine Umgehung für eine unzureichend starke Überprüfung bei der Kompilierung, und einige Sprachen sind in dieser ( en.wikipedia.org/wiki/Agda_(programming_language)
Den
21

Ja. Wenn wir mit 100% iger Sicherheit sagen könnten: Diese Funktion wird niemals bearbeitet und wird niemals in einem Kontext ausgeführt, der zum Fehlschlagen führen könnte. Wenn wir das sagen könnten, könnten wir die Tests verwerfen und bei jedem Vorgang ein paar Millisekunden sparen CI bauen.

Aber wir können nicht. Oder wir können nicht mit vielen Funktionen. Und es ist einfacher, eine Regel zu haben, nach der alle Tests die ganze Zeit ausgeführt werden, als genau zu bestimmen, mit welcher Vertrauensschwelle wir zufrieden sind und wie viel Vertrauen wir in die Unveränderlichkeit und Unfehlbarkeit einer bestimmten Funktion haben.

Und die Bearbeitungszeit ist günstig. Diese Millisekunden, die gespart und sogar um ein Vielfaches vervielfacht wurden, reichen nicht aus, um sich bei jeder Funktion die Zeit zu nehmen und zu fragen: Sind wir sicher genug, dass wir sie nie wieder testen müssen?

Carl Manaster
quelle
Ich denke, das ist ein sehr guter Punkt, den Sie hier ansprechen. Ich kann bereits sehen, wie zwei Leute stundenlang darüber streiten, Tests für einige kleine Funktionen durchzuführen.
Kapol
@Kapol: kein Streit, ein Treffen, um zu bestimmen, welche gehen und welche bleiben können, und welche Kriterien zur Entscheidung herangezogen werden sollten, sowie wo die Kriterien zu dokumentieren sind und wer die endgültige Entscheidung unterschreibt ...
jmoreno
12

Alles, was in den anderen Antworten gesagt wurde, ist richtig, aber ich werde noch eine hinzufügen.

Dokumentation

Unit-Tests können einem Entwickler, wenn sie gut geschrieben sind, genau erklären, was eine Funktion tut, welche Eingabe- / Ausgabeerwartungen sie hat und was noch wichtiger ist, welches Verhalten von ihr erwartet werden kann.

Dies kann das Erkennen eines Fehlers erleichtern und die Verwirrung verringern.

Nicht jeder merkt sich Polynome, Geometrie oder sogar Algebra :) Aber ein guter Komponententest, der zur Versionskontrolle eingecheckt wurde, wird sich für mich daran erinnern.

Ein Beispiel dafür, wie nützlich es als Dokumentation sein kann, finden Sie in der Jasmine-Einführung: http://jasmine.github.io/edge/introduction.html Nehmen Sie sich ein paar Sekunden Zeit zum Laden und scrollen Sie dann nach unten. Sie sehen die gesamte Jasmine-API als Unit-Test-Ausgabe.

[Aktualisierung basierend auf dem Feedback von @Warbo] Tests sind garantiert ebenfalls auf dem neuesten Stand, da sie sonst fehlschlagen, was im Allgemeinen zu einem Build-Fehler führt, wenn CI verwendet wird. Die externe Dokumentation ändert sich unabhängig vom Code und ist daher nicht unbedingt auf dem neuesten Stand.

Brandon
quelle
1
Es ist ein großer Vorteil, Tests als (einen Teil der) Dokumentation zu verwenden, die Sie implizit hinterlassen haben: Sie werden automatisch überprüft. Andere Formen der Dokumentation, z. Erklärende Kommentare oder Code-Schnipsel auf einer Webseite können veraltet sein, wenn sich der Code weiterentwickelt. Unit-Tests lösen einen Fehler aus, wenn sie nicht mehr korrekt sind. Diese Jasmin-Seite ist ein extremes Beispiel dafür.
Warbo
Ich möchte hinzufügen, dass einige Sprachen, wie D und Rust, ihre Dokumentationsgenerierung in ihre Unit-Tests integrieren, sodass Sie denselben Code in einen Unit-Test kompilieren und in die HTML-Dokumentation einfügen können
Idan Arye
2

Reality-Check

Ich war in herausfordernden Umgebungen, in denen das Testen "Zeitverschwendung" während der Budgetierung und des Zeitplans ist, und dann "ein grundlegender Teil der Qualitätssicherung", sobald der Kunde mit Fehlern zu tun hat. Meine Meinung ist also flüssiger als die anderer.

Sie haben ein Budget. Ihre Aufgabe ist es, das bestmögliche Produkt für dieses Budget zu erhalten, unabhängig von der Definition des "besten", das Sie zusammenkratzen können (es ist kein leicht zu definierendes Wort). Ende der Geschichte.

Testen ist ein Werkzeug in Ihrem Inventar. Sie sollten es verwenden, weil es ein gutes Werkzeug ist , mit einer langen Geschichte des Sparens von Millionen oder vielleicht sogar Milliarden von Dollar. Wenn Sie eine Chance haben, sollten Sie diesen einfachen Funktionen Tests hinzufügen. Es kann Ihre Haut eines Tages retten.

In der realen Welt kann dies jedoch aufgrund von Budget- und Zeitplanbeschränkungen nicht passieren. Machen Sie sich nicht zum Sklaven des Verfahrens. Testfunktionen sind nett, aber irgendwann sollten Sie die Entwicklerdokumentation besser in Worten als in Code schreiben, damit der nächste Entwickler nicht so viele Tests benötigt. Oder es könnte besser sein, die Codebasis umzugestalten, damit Sie nicht so schwierig wie ein Biest bleiben müssen. Oder vielleicht ist es besser, diese Zeit damit zu verbringen, mit Ihrem Chef über das Budget und den Zeitplan zu sprechen, damit er oder sie besser versteht, worum es bei der nächsten Finanzierungsrunde geht.

Softwareentwicklung ist ein Gleichgewicht. Zählen Sie immer die Opportunitätskosten für alles, was Sie tun, um sicherzustellen, dass es keinen besseren Weg gibt, Ihre Zeit zu verbringen.

Cort Ammon
quelle
4
In diesem Fall gab es bereits einen Test. Die Frage ist also nicht, ob sie geschrieben werden sollen, sondern ob sie festgeschrieben und mit jedem Build oder Release ausgeführt werden sollen oder nach welchem ​​Zeitplan alle Tests ausgeführt werden sollen.
Paŭlo Ebermann
0

Ja, halten Sie die Tests aufrecht, lassen Sie sie weiterlaufen und bestehen Sie sie.

Unit-Tests sollen Sie (und andere) vor sich selbst (und sich selbst) schützen.

Warum ist es eine gute Idee, die Tests beizubehalten?

  • Validieren Sie die Funktionalität der vorherigen Anforderungen angesichts neuer Anforderungen und zusätzlicher Funktionen
  • Stellen Sie sicher, dass die Refactoring-Übungen korrekt sind
  • Interne Dokumentation - so soll der Code verwendet werden
  • Regressionstests, Dinge ändern sich
    • Brechen die Änderungen den alten Code?
    • Benötigen die Änderungen weitere Änderungsanforderungen oder Aktualisierungen der aktuellen Funktionen oder des Codes?
  • Da die Tests bereits geschrieben sind, bewahren Sie sie auf. Es ist Zeit und Geld, die bereits aufgewendet wurden, um die Wartungskosten zu senken
Niall
quelle
2
Dies scheint nichts Wesentliches zu bieten als die anderen Antworten, die bereitgestellt wurden.
1
Es gab ein Problem beim Posten, aber im Nachhinein ist es eine schöne Zusammenfassung. Ich werde es entfernen.
Niall
-1

Entwicklerdokumentation

  • Woher weiß ich (als anderer Entwickler), dass dies getestet wurde?
  • Wenn ich einen Fehler in der eigenständigen Funktion beheben möchte , woher weiß ich, dass ich keinen Fehler einführe, den Sie bereits berücksichtigt haben?
  • Komplexitätsindikator: Die Anzahl der Tests kann ein gutes Maß dafür sein, wie komplex etwas ist. Dies kann bedeuten, dass Sie es nicht berühren sollten, da es ausgereift und stabil ist oder aufgebläht und gekoppelt ist.

Benutzerdokumentation

  • In Erwartung eines schlechten Dokuments kann ich prüfen, ob {Null, negative Werte, leere Mengen usw.} akzeptiert werden und wie hoch der erwartete Rückgabewert ist.
  • Es gibt auch ein gutes Beispiel dafür, wie ich das Objekt / die Funktion verwenden soll
Sixtyfootersdude
quelle
1
Dies scheint nichts Wesentliches über die in früheren Antworten gemachten und erklärten Punkte zu bieten. Sogar "nette Zusammenfassung" wurde bereits gepostet (nach meinem Geschmack ist es nicht so schön, aber na ja)
gnat