Seien Sie am 30. April dabei: Vorstellung von Parasoft C/C++test CT für kontinuierliche Tests und Compliance-Exzellenz | Registrierung

Best Practices für Unit-Tests: So holen Sie das Beste aus Ihrer Testautomatisierung heraus

Kopfschuss von Brian McGlauflin,
14. Mai 2019
8 min lesen

Unit-Tests helfen Unternehmen, ihre Workloads Einheit für Einheit zu testen, aber die Frage ist, wie man Unit-Tests am besten angeht. Sehen Sie sich diesen Beitrag an, um Best Practices für Unit-Tests zu erfahren.

Komponententests sind eine bekannte Praxis, aber es gibt viel Raum für Verbesserungen! Dieser Beitrag behandelt die effektivsten Best Practices für Unit-Tests, einschließlich Ansätzen zur Maximierung Ihrer Automatisierungstools auf dem Weg dorthin. Wir werden auch die Codeabdeckung, das Verspotten von Abhängigkeiten und allgemeine Teststrategien besprechen.

Was ist Unit Testing?

Unit-Tests sind die Praxis, einzelne Units oder Komponenten einer Anwendung zu testen, um zu validieren, dass jede dieser Units ordnungsgemäß funktioniert. Im Allgemeinen sollte eine Unit ein kleiner Teil der Anwendung sein – in Java ist es oft eine einzelne Klasse. Beachten Sie, dass ich „Einheit“ hier nicht streng definiere und es Sache des Entwicklers ist, den Umfang des getesteten Codes für jeden Test festzulegen.

Menschen kontrastieren manchmal den Begriff "Unit-Test" mit "Integrationstest" oder "End-to-End-Test". Der Unterschied besteht darin, dass im Allgemeinen Unit-Tests durchgeführt werden, um das Verhalten einer einzelnen testbaren Einheit zu validieren, während Integrationstests das Verhalten mehrerer Komponenten zusammen oder der gesamten Anwendung validieren. Wie ich bereits sagte, ist die Definition für eine „Einheit“ nicht streng definiert, und es liegt an Ihnen, den Umfang für jeden Test zu bestimmen.

Warum Unit Test?

Unit-Tests sind eine bewährte Technik zur Sicherstellung der Softwarequalität mit zahlreichen Vorteilen. Hier sind (mehr als) einige gute Gründe für einen Unit-Test:

  • Unit-Test validiert dass jedes Teil Ihrer Software nicht nur heute richtig funktioniert, sondern auch in Zukunft funktioniert und eine solide Grundlage für die zukünftige Entwicklung bietet.
  • Unit-Test erkennt Mängel in einem frühen Stadium des Produktionsprozesses, die reduziert die Kosten von ihnen in späteren Phasen des Entwicklungszyklus zu beheben.
  • Unit-getesteter Code ist in der Regel sicherer zu refactor, da Tests schnell erneut ausgeführt werden können, um zu überprüfen, ob sich das Verhalten nicht geändert hat.
  • Das Schreiben von Komponententests zwingt Entwickler zu überlegen, wie gut der Produktionscode entworfen wurde, um ihn zu erstellen geeignet für Unit-Testsund lässt Entwickler ihren Code von a aus betrachten andere Perspektiveund ermutigt sie, Eckfälle und Fehlerbedingungen bei ihrer Implementierung zu berücksichtigen.
  • Einschließlich Unit-Tests in der Codeüberprüfungsprozess kann zeigen, wie der geänderte oder neue Code funktionieren soll. Außerdem können Prüfer bestätigen, ob die Tests gut sind oder nicht.

Es ist bedauerlich, dass Entwickler allzu oft überhaupt keine Komponententests schreiben, nicht genug Tests schreiben oder sie nicht warten. Ich verstehe - Unit-Tests können manchmal schwierig zu schreiben oder zeitaufwändig zu warten sein. Manchmal gibt es eine Frist, die eingehalten werden muss, und es scheint, als würden wir diese Frist verpassen, wenn wir Tests schreiben. Aber nicht genug Unit-Tests zu schreiben oder keine guten Unit-Tests zu schreiben, ist eine riskante Falle, in die man geraten kann.

Beachten Sie daher bitte meine folgenden Best-Practice-Empfehlungen zum Schreiben sauberer, wartbarer und automatisierter Tests, die Ihnen alle Vorteile von Komponententests mit minimalem Zeit- und Arbeitsaufwand bieten.

Best Practices für Unit-Tests

Schauen wir uns einige Best Practices zum Erstellen, Ausführen und Verwalten von Komponententests an, um die besten Ergebnisse zu erzielen.

Komponententests sollten vertrauenswürdig sein

Der Test muss fehlschlagen, wenn der Code fehlerhaft ist und nur, wenn der Code fehlerhaft ist. Wenn nicht, können wir nicht vertrauen, was die Testergebnisse uns sagen.

Komponententests sollten wartbar und lesbar sein

Wenn sich der Produktionscode ändert, müssen Tests häufig aktualisiert und möglicherweise auch debuggt werden. Es muss also einfach sein, den Test zu lesen und zu verstehen, nicht nur für jeden, der ihn geschrieben hat, sondern auch für andere Entwickler. Organisieren und benennen Sie Ihre Tests immer, um Klarheit und Lesbarkeit zu gewährleisten.

Unit-Tests sollten einen einzelnen Anwendungsfall bestätigen

Gute Tests validieren eine Sache und nur eine Sache, was bedeutet, dass sie normalerweise einen einzelnen Anwendungsfall validieren. Tests, die dieser bewährten Methode folgen, sind einfacher und verständlicher. Dies ist gut für die Wartbarkeit und das Debuggen. Tests, die mehr als eine Sache validieren, können leicht komplex und zeitaufwändig werden. Lass das nicht zu.

Eine weitere bewährte Methode besteht darin, eine minimale Anzahl von Zusicherungen zu verwenden. Einige Leute empfehlen nur eine Behauptung pro Test (dies kann etwas zu restriktiv sein); Die Idee ist, sich darauf zu konzentrieren, nur das zu validieren, was für den von Ihnen getesteten Anwendungsfall benötigt wird.

Komponententests sollten isoliert werden

Tests sollten auf jeder Maschine in beliebiger Reihenfolge ausgeführt werden können, ohne sich gegenseitig zu beeinflussen. Tests sollten nach Möglichkeit keine Abhängigkeiten von Umweltfaktoren oder dem globalen / externen Zustand aufweisen. Tests mit diesen Abhängigkeiten sind schwieriger auszuführen und normalerweise instabil, was das Debuggen und Beheben von Tests erschwert und am Ende mehr Zeit kostet als sie sparen (siehe) vertrauenswürdig, über).

Martin Fowler schrieb vor einigen Jahren über „einsamen“ und „geselligen“ Code, um die Verwendung von Abhängigkeiten im Anwendungscode zu beschreiben und wie Tests entsprechend gestaltet werden müssen. In seinem Artikel hängt „einsamer“ Code nicht von anderen Einheiten ab (er ist eigenständiger), wohingegen „geselliger“ Code mit anderen Komponenten interagiert. Wenn der Anwendungscode einsam ist, ist der Test einfach… aber für den zu testenden geselligen Code können Sie entweder einen „einsamen“ oder einen „geselligen“ Test erstellen. Ein „geselliger Test“ würde sich auf reale Abhängigkeiten stützen, um das Verhalten zu validieren, während ein „Einzeltest“ den zu testenden Code von Abhängigkeiten isoliert. Sie können Mocks verwenden, um den zu testenden Code zu isolieren und einen "einsamen" Test für "geselligen" Code zu erstellen. Wir werden uns unten ansehen, wie das geht.

Im Allgemeinen erleichtert die Verwendung von Mocks für Abhängigkeiten unser Leben als Tester, da wir „Einzelprüfungen“ für geselligen Code erstellen können. Ein geselliger Test für komplexen Code erfordert möglicherweise viel Setup und verstößt möglicherweise gegen die Prinzipien der Isolation und Wiederholbarkeit. Da das Modell jedoch im Test erstellt und konfiguriert wird, ist es in sich geschlossen und wir haben mehr Kontrolle über das Verhalten von Abhängigkeiten. Außerdem können wir weitere Codepfade testen. Zum Beispiel kann ich benutzerdefinierte Werte zurückgeben oder Ausnahmen vom Mock auslösen, um Rand- oder Fehlerbedingungen abzudecken.

Unit-Tests sollten automatisiert werden

Stelle sicher, dass Führen Sie Unit-Tests in einem automatisierten Prozess durch. Dies kann täglich oder stündlich oder in einem kontinuierlichen Integrations- oder Bereitstellungsprozess erfolgen. Die Berichte müssen für alle im Team zugänglich und einsehbar sein. Sprechen Sie im Team darüber, welche Metriken Ihnen wichtig sind: Codeabdeckung, modifizierte Codeabdeckung, Anzahl der ausgeführten Tests, Leistung usw.

Wenn man sich diese Zahlen ansieht, kann man viel lernen, und eine große Verschiebung dieser Zahlen weist oft auf Regressionen hin, die sofort angegangen werden können.

Ebook: Verbessern Sie Unit-Tests für Java mit Automatisierung

Verwenden Sie eine gute Mischung aus Unit- und Integrationstests

Michael Cohns Buch, Erfolg mit Agile: Softwareentwicklung mit Scrum, behebt dies mithilfe eines Testpyramidenmodells (siehe Abbildung im Bild unten). Dies ist ein häufig verwendetes Modell zur Beschreibung der idealen Verteilung von Testressourcen. Die Idee ist, dass Tests in der Regel komplexer zu erstellen, anfälliger, langsamer auszuführen und langsamer zu debuggen sind, wenn Sie in die Pyramide aufsteigen. Niedrigere Ebenen sind isolierter und integrierter, schneller und einfacher zu erstellen und zu debuggen. Daher sollten automatisierte Komponententests den Großteil Ihrer Tests ausmachen.

Unit-Tests sollten alle Details, Eckfälle und Randbedingungen usw. validieren. Komponenten-, Integrations-, UI- und Funktionstests sollten sparsamer verwendet werden, um das Verhalten der APIs oder der gesamten Anwendung zu validieren. Manuelle Tests sollten einen minimalen Prozentsatz der gesamten Pyramidenstruktur ausmachen, sind jedoch weiterhin für die Akzeptanz von Freisetzungen und Erkundungstests nützlich. Dieses Modell bietet Unternehmen ein hohes Maß an Automatisierung und Testabdeckung, sodass sie ihre Testbemühungen ausweiten und die mit dem Erstellen, Ausführen und Verwalten von Tests verbundenen Kosten auf ein Minimum beschränken können.

Unit-Tests sollten innerhalb einer organisierten Testpraxis durchgeführt werden

Um den Erfolg Ihrer Tests auf allen Ebenen zu steigern und den Unit-Test-Prozess skalierbar und nachhaltig zu gestalten, benötigen Sie einige zusätzliche Vorgehensweisen. Zuallererst bedeutet dies Schreiben von Komponententests beim Schreiben Ihres Anwendungscodes. Einige Organisationen schreiben die Tests vor dem Anwendungscode (testgetrieben or verhaltensgetrieben Programmierung). Wichtig ist, dass Tests mit dem Anwendungscode Hand in Hand gehen. Die Tests und der Anwendungscode sollten sogar gemeinsam im Codeüberprüfungsprozess überprüft werden. Bewertungen helfen Ihnen, den geschriebenen Code zu verstehen (weil sie das erwartete Verhalten erkennen können) und verbessern auch Tests!

Das Schreiben von Tests zusammen mit Code ist nicht nur für neues Verhalten oder geplante Änderungen gedacht, sondern auch für Fehlerkorrekturen. Jeder Fehler, den Sie beheben, sollte einen Test haben, der überprüft, ob der Fehler behoben ist. Dies stellt sicher, dass der Fehler auch in Zukunft behoben bleibt.

Nehmen Sie eine Null-Toleranz-Richtlinie für fehlgeschlagene Tests an. Wenn Ihr Team die Testergebnisse ignoriert, warum dann überhaupt Tests? Testfehler sollten auf echte Probleme hinweisen. Beheben Sie diese Probleme daher sofort, bevor sie die Zeit der Qualitätssicherung verschwenden, oder schlimmer noch, sie gelangen in das freigegebene Produkt.

Je länger es dauert, um Fehler zu beheben, desto mehr Zeit und Geld kosten diese Fehler letztendlich Ihr Unternehmen. Führen Sie also Tests während des Refactorings aus, führen Sie Tests direkt vor dem Festschreiben des Codes aus und lassen Sie eine Aufgabe erst dann als "erledigt" gelten, wenn auch die Tests bestanden wurden.

Schließlich Behalten Sie diese Tests bei. Wie ich bereits sagte, verlieren Sie ihren Wert, wenn Sie diese Tests nicht auf dem neuesten Stand halten, wenn sich die Anwendung ändert. Insbesondere wenn sie fehlschlagen, kosten fehlgeschlagene Tests Zeit und Geld, um sie jedes Mal zu untersuchen, wenn sie fehlschlagen. Refaktorieren Sie die Tests nach Bedarf, wenn sich der Code ändert.

Wie Sie sehen können, erfordert die Maximierung Ihrer Geldrendite und der Zeit, die Sie in Ihre Komponententests investieren, einige Investitionen in die Anwendung von Best Practices. Aber am Ende sind die Belohnungen die anfängliche Investition wert.

Was ist mit Code Coverage?

Im Allgemeinen ist die Codeabdeckung ein Maß dafür, wie viel des Produktionscodes ausgeführt wird, während Ihre automatisierten Tests ausgeführt werden. Wenn Sie eine Reihe von Tests ausführen und die Daten zur Codeabdeckung anzeigen, können Sie einen allgemeinen Eindruck davon bekommen, wie viel von Ihrer Anwendung getestet wird.

Es gibt viele Arten der Codeabdeckung - die häufigsten sind Leitungsabdeckung und Zweigabdeckung. Die meisten Tools konzentrieren sich auf die Linienabdeckung, die nur angibt, ob eine bestimmte Linie abgedeckt wurde. Der Zweig ist detaillierter, da er Ihnen sagt, ob jeder Pfad durch den Code abgedeckt ist.

Die Codeabdeckung ist eine wichtige Messgröße, aber denken Sie daran, dass eine Erhöhung ein Mittel zum Zweck ist. Es ist großartig, um Lücken beim Testen zu finden, aber es ist nicht das einzige, worauf man sich konzentrieren muss. Achten Sie darauf, nicht zu viel Aufwand zu betreiben, um eine 100% ige Abdeckung zu erreichen - dies ist möglicherweise nicht möglich oder machbar, und die Qualität Ihrer Tests ist wirklich wichtig. Abgesehen davon ist es ein guter Ausgangspunkt, eine Abdeckung von mindestens 60% für Ihre Projekte zu erreichen, und 80% oder mehr sind ein gutes Ziel. Natürlich liegt es an Ihnen, zu entscheiden, was dieses Ziel sein soll.

Dies ist auch hilfreich, wenn Sie über automatisierte Tools verfügen, die nicht nur die Codeabdeckung messen, sondern auch verfolgen, wie viel geänderter Code von Tests abgedeckt wird, da dies Aufschluss darüber gibt, ob genügend Tests zusammen mit Änderungen im Produktionscode geschrieben werden.

Hier finden Sie einen Beispielbericht zur Codeabdeckung aus dem Berichts- und Analyse-Hub von Parasoft, durch den Sie navigieren können, wenn Sie ihn verwenden Parasoft Jtest für Ihre Unit-Test:

Beachten Sie außerdem, dass Sie sich beim Schreiben neuer Tests darauf konzentrieren sollten, sich nur auf die Zeilenabdeckung zu konzentrieren, da einzelne Codezeilen zu mehreren Codepfaden führen können. Stellen Sie daher sicher, dass Ihre Tests diese Codepfade validieren. Die Leitungsabdeckung ist ein nützlicher Schnellindikator, aber nicht das einzige, wonach Sie suchen müssen.

Der naheliegendste Weg, um die Abdeckung zu erhöhen, besteht einfach darin, mehr Tests für mehr Codepfade und mehr Anwendungsfälle für die zu testende Methode hinzuzufügen. Eine leistungsstarke Methode zur Erhöhung der Abdeckung ist die Verwendung parametrisierter Tests. Für Junit4 gab es die integrierte Junit4-Parameterfunktionalität und Bibliotheken von Drittanbietern wie JUnitParams. JUnit3 verfügt über eine integrierte Parametrierung.

Wenn Sie die Testabdeckung noch nicht verfolgen, empfehle ich Ihnen dringend, zu beginnen. Es gibt viele Tools, die helfen können, wie z Parasoft Jtest. Beginnen Sie mit der Messung Ihrer aktuellen Abdeckungszahlen, legen Sie dann Ziele fest, wo sie sein sollten, schließen Sie zuerst wichtige Lücken und arbeiten Sie dann von dort aus.

Zusammenfassung

Obwohl Unit-Tests eine bewährte Technik zur Sicherstellung der Softwarequalität sind, werden sie immer noch als Belastung für Entwickler angesehen, und viele Teams haben immer noch Probleme damit. Um Tests und automatisierte Testtools optimal nutzen zu können, müssen Tests vertrauenswürdig, wartbar, lesbar und in sich geschlossen sein und zur Überprüfung eines einzelnen Anwendungsfalls verwendet werden. Automatisierung ist der Schlüssel, um Unit-Tests funktionsfähig und skalierbar zu machen.

Darüber hinaus müssen Softwareteams gute Testtechniken üben, z. B. das Schreiben und Überprüfen von Tests neben dem Anwendungscode, das Verwalten von Tests und das sofortige Verfolgen und Beheben fehlgeschlagener Tests. Durch die Übernahme dieser Best Practices für Komponententests können Sie die Ergebnisse Ihrer Komponententests schnell verbessern.

Erfahren Sie, wie Sie mit Parasoft Jtest Ihre Rendite aus Unit-Tests steigern können.