Wie überprüfen Sie, ob der Code automatisch abgedeckt wurde?

9

Ich bin dabei, einen Bamboo-Server für einige neue Projekte für einen Push an TDD in einem CI / CD-Workflow einzurichten. Sicher, Unit-Tests sind großartig, aber nur so protokolliert, wie sie vorhanden sind.

Dies ist möglicherweise besser in einem GIT-Pre-Recieve-Hook für bestimmte Zweige (z. B. Entwicklungs- und Hauptversionszweige), aber wie sollte die Codeabdeckung erzwungen werden, wenn überhaupt. Ich bin froh, den Committern vertrauen zu können, um sicherzustellen, dass der Code abgedeckt ist. Aber wie werden diese Dinge aufrecht erhalten, ohne dass es zu Abweichungen von Sorgfalt und Konsistenz kommt?

Kurz gesagt, ich möchte sehen, wie andere die Testabdeckung als automatischen Prozess während der Festschreibungs- oder Erstellungsphase erzwingen.

Daniel Park
quelle

Antworten:

17

Sie sollten die Codeabdeckung nicht automatisch erzwingen.

Dies ist wie das Erzwingen der maximalen Codezeilen pro Methode: Einverstanden, die meisten Methoden sollten weniger als beispielsweise 20 LOC betragen, aber es gibt gültige Fälle, in denen Methoden länger wären.

Auf die gleiche Weise kann die Ausrichtung auf einen bestimmten Prozentsatz der Codeabdeckung pro Klasse zu unerwünschten Konsequenzen führen. Zum Beispiel:

  • Boilerplate-Codeklassen oder Klassen, die von Codegeneratoren erstellt wurden, werden möglicherweise überhaupt nicht getestet. Entwickler zu zwingen, sie zu testen, hat keinen Vorteil und verursacht erhebliche Kosten in Bezug auf die dafür aufgewendete Zeit.

  • Einfache Code-Behandlung unwichtiger Teile der Anwendung muss nicht unbedingt getestet werden.

  • In einigen Sprachen kann ein Teil des Codes nicht getestet werden. Ich hatte diesen Fall in C # mit anonymen Methoden in einer Bibliothek, in der ich wirklich eine 100% ige Codeabdeckung haben wollte . Diese Fälle können für Entwickler demoralisierend sein.

Noch wichtiger ist, dass die Codeabdeckung proportional zu zwei Aspekten des Codes sein sollte: wie kritisch und wie kompliziert es ist :

  • Ein Teil des Codes mit einer komplizierten Logik, die Teil des Hauptmerkmals einer Anwendung ist, sollte besser sorgfältig getestet werden, da Fehler oder Regressionen wichtige Konsequenzen haben können.

  • Ein einfacher Code, der eine Funktion behandelt, die niemand verwendet, enthält möglicherweise grundlegende Tests, die nur grundlegende Fälle abdecken.

Natürlich können Sie die Codeabdeckung weiterhin als Maß verwenden , insbesondere um zu vergleichen, wie verschiedene Teams die Codeabdeckung erreichen: Es kann Teams geben, die weniger diszipliniert und beim Testen weniger zurückhaltend sind. In diesen Fällen möchten Sie diese Metrik möglicherweise mit anderen kombinieren, z. B. der Anzahl der Fehler, der Zeit, die für die Behebung von Fehlern aufgewendet wurde, oder der Anzahl der Anmerkungen während der Codeüberprüfung.

Möglicherweise möchten Sie auch bei einzelnen Projekten, bei denen dies sinnvoll ist (achten Sie darauf, Prototypen, generierten Code, CRUD usw. auszuschließen), zumindest eine gewisse Codeabdeckung (z. B. 60% ¹) erzwingen , damit Entwickler bestimmte Klassen als ausgeschlossen markieren können von der Codeabdeckung ist auch schön². In diesem Fall kann dies im Rahmen einer Überprüfung erfolgen, bei der ein Build fehlschlägt, wenn die Codeabdeckung unter dem erforderlichen Minimum liegt. Ich würde es in der Build-Phase tun, nicht in der Commit-Phase , da nicht erwartet wird, dass Sie während des Commits Unit-Tests ausführen .


¹ Ich würde 60% als angemessenes Minimum betrachten, basierend auf meiner Codebasis: Fast jedes Projekt oder jede Klasse mit weniger als 60% Codeabdeckung ist wirklich ungetestet . Dies kann von Sprache zu Sprache und von Unternehmen zu Unternehmen sehr unterschiedlich sein (in einigen Unternehmen sind 0% ein Standard). Besprechen Sie mit Ihrem Team, was normal und was für sie zu hoch ist . Vielleicht erreichen sie ständig 95% und können leicht 99% erreichen, oder sie haben Schwierigkeiten, ihre Codeabdeckung von 70% auf 75% zu erhöhen.

² Angesichts der Tatsache, dass bei Codeüberprüfungen eventueller Missbrauch festgestellt wird, sollten Sie keine Angst haben, Entwicklern diese Möglichkeit zu geben. Dies ähnelt der Möglichkeit, einige Teile des Codes von den Überprüfungen durch die Linters oder Stilprüfer auszuschließen. JSLint, StyleCop und Code Analysis sind drei Beispiele, bei denen der Ausschluss unterstützt wird und tatsächlich nützlich ist, ohne Missbrauch zu fördern.

Arseni Mourzenko
quelle
Ich verstehe vollkommen, dass die Durchsetzung einiger Abschlussregeln eine kontraproduktive, wenn nicht sogar unmögliche Leistung ist. Es scheint, als hätte ich hier mit einer Lösung zu technisch gedacht, und vielleicht kommt es darauf an, dass einige Schritte vor dem, was ich oben besprochen habe, Überarbeitungspraktiken durchgeführt werden, z. B. in einer Pull-Request-Phase. Ich hatte die Idee, dass dies zu streng sei, wollte aber überprüfen, ob jemand Methoden in der Praxis hat.
Daniel Park
1
@ DanielPark-Pull-Anfragen sind ein wichtiger Bestandteil des GitHub-Workflows IMO.
RubberDuck
Ich werde diese Antwort aufgrund Ihrer ersten Schlussfolgerungen zum Berichterstattungskontext als Antwort gegenüber anderen markieren. Ich habe auch den Punkt weggenommen, dass die Codeabdeckung am besten als Messung und nicht als Kriterium verwendet wird und dass die Bedingungen im Allgemeinen sehr subjektiv und für sich genommen nicht übermäßig konstruktiv sind. Ich denke, meine Richtung in dieser Phase ist es, die Metrik in Pull-Request-Phasen einzubeziehen und zusätzlich sorgfältig darauf zu achten, dass vor den veröffentlichten Veröffentlichungen eine angemessene Abdeckung in den richtigen Bereichen erfolgt. Vielen Dank für alle Antworten.
Daniel Park
"Ein einfacher Code, der eine Funktion behandelt, die niemand verwendet", sollte vielleicht stattdessen entfernt werden?
rjnilsson
4

Betrachten Sie den folgenden Code:

rsq = a*a + b*b;
if (rsq >= 0.0) {
    r = sqrt(rsq);
}
else {
    handle_this_impossible_event();
}

Es gibt keine Möglichkeit, einen Testfall zu erstellen, der diesen anderen Zweig erreicht. Wenn dies jedoch eine sicherheitskritische Flugsoftware wäre, wären die Leute im ganzen Fall des Autors, wenn dieser Schutz gegen das Senden eines negativen Wertes sqrtnicht vorhanden wäre. Typischerweise werden die Berechnung rsq = a*a + b*bund die Extraktion der Quadratwurzel durch mehrere Codezeilen getrennt. In der Zwischenzeit kann ein kosmischer Strahl das Vorzeichenbit einschalten rsq.

Tatsächlich hat die Flugsoftware das Äquivalent von handle_this_impossible_event()mehrfach aufgerufen . In der Regel müssen Sie die Steuerung auf einen redundanten Computer umstellen, den verdächtigen Computer ordnungsgemäß herunterfahren, den verdächtigen Computer neu starten und schließlich den verdächtigen Computer als Backup übernehmen. Das ist viel besser als der primäre Flugcomputer, der verrückt wird.

Selbst in Flugsoftware ist es unmöglich, eine 100% ige Codeabdeckung zu erreichen. Die Leute, die behaupten, dies erreicht zu haben, haben entweder einen trivialen Code oder sie haben nicht genug Tests gegen diese unmöglichen Ereignisse.

David Hammen
quelle
Wenn aund bvorzeichenbehaftete 32-Bit-Ganzzahlen sind, können viele Werte wie 65535 und 65535 oder 40000 und 40000 rsqnegativ sein.
David Conrad
2
@DavidConrad - Ich habe verwendet 0.0, was impliziert, aund bsind einige Gleitkomma-Typ. Bei Flugsoftware ist es unerlässlich, sich vor der Quadratwurzel einer negativen Zahl zu schützen, auch wenn Sie wissen, dass die Zahl nicht negativ sein kann. Kosmische Strahlung sind lästige Kleinigkeiten. In einem kürzlich durchgeführten Experiment ( das vor weniger als einer Woche gestartet wurde ) wird untersucht, ob ein Supercomputer auf der Internationalen Raumstation Software anstelle von Hardware zum Schutz vor kosmischer Strahlung (SEUs usw.) verwenden kann.
David Hammen
Fair genug, ich habe das übersehen 0.0.
David Conrad
4

Die Testabdeckung ist eine nützliche Messgröße für den Gesamtzustand Ihres Projekts. Mit einer hohen Testabdeckung können Sie eine fundierte Entscheidung darüber treffen, ob die Software bei der Bereitstellung wie erwartet funktioniert. mit einer geringen Testabdeckung bedeutet dies, dass Sie nur raten. Es gibt Tools zum automatischen Messen der Abdeckung. Diese funktionieren normalerweise, indem das Programm in einem Debugging-Kontext ausgeführt wird oder indem Buchhaltungsvorgänge in den ausgeführten Code eingefügt werden.

Es gibt verschiedene Arten von Tests und verschiedene Arten von Abdeckungsmetriken. Zu den allgemeinen Abdeckungsmetriken gehören Funktionsabdeckung, Anweisungsabdeckung, Zweigabdeckung und Bedingungsabdeckung, obwohl es mehr gibt .

  • Einheit Tests prüfen , ob die Durchführung eines konzeptuellen Einheit (Modul, Klassen-, Methoden-, ...) sich nach seiner Spezifikation (in TDD, der Test ist die Spezifikation). Einheiten ohne eigene Komponententests sind eine rote Fahne, obwohl sie möglicherweise durch Tests im Integrationsstil abgedeckt werden.

    Unit-Tests sollten eine nahezu vollständige Funktionsabdeckung implizieren. Da der Unit-Test die gesamte öffentliche Schnittstelle dieser Unit ausübt, sollte es keine Funktionen geben, die von diesen Tests nicht berührt werden. Wenn Sie Unit-Tests in eine vorhandene Codebasis einführen, ist die Funktionsabdeckung ein grober Fortschrittsindikator.

    Ein Unit-Test sollte eine gute Abdeckung (75% –100%) anstreben. Die Anweisungsabdeckung ist eine Qualitätsmetrik für einen Komponententest. Eine vollständige Abdeckung ist nicht immer möglich, und Sie können Ihre Zeit wahrscheinlich besser nutzen, als die Abdeckung über 95% hinaus zu verbessern.

    Zweig- und Zustandsabdeckung sind schwieriger. Je komplizierter oder wichtiger ein Code ist, desto höher sollten diese Metriken sein. Bei unspektakulärem Code reicht jedoch in der Regel eine hohe Abdeckung der Anweisungen aus (und dies impliziert bereits eine Zweigabdeckung von mindestens 50%). Ein Blick auf den Zustandsbericht einer Einheit kann dazu beitragen, bessere Testfälle zu erstellen.

  • Integrationstests prüfen, ob mehrere Einheiten korrekt miteinander arbeiten können. Integrationstests können sehr nützlich sein, ohne in einer Abdeckungsmetrik eine hohe Punktzahl zu erzielen. Während Integrationstests normalerweise einen großen Teil der Schnittstellen ihrer Einheit beanspruchen (dh eine hohe Funktionsabdeckung aufweisen), wurden die Interna dieser Einheiten bereits durch die Einheitentests abgedeckt.

Es ist eine gute Idee, Tests auszuführen, bevor Code in einem Hauptzweig zusammengeführt wird. Die Berechnung der Testabdeckungsmetriken für das gesamte Programm nimmt jedoch viel Zeit in Anspruch - dies ist eine gute Aufgabe für einen nächtlichen Build. Wenn Sie herausfinden können, wie dies zu tun ist, besteht ein guter Kompromiss darin, nur geänderte Tests oder Komponententests für geänderte Einheiten in einem Git-Hook auszuführen. Testfehler sind für nichts anderes als "Work in Progress" -Commits akzeptabel. Wenn ausgewählte Abdeckungsmetriken einen bestimmten Schwellenwert unterschreiten (z. B. Abdeckungsabdeckung unter 80% oder Einführung neuer Methoden ohne entsprechende Tests), sollten diese Probleme als Warnung behandelt werden, mit der Möglichkeit für den Entwickler, diese potenziellen Probleme zu beheben. Manchmal gibt es jedoch gute Gründe, diese Warnungen zu ignorieren, und Entwickler sollten dazu in der Lage sein.

Testen ist gut, aber zu viel davon kann nervig werden. Schnelles, relevantes Feedback kann dazu beitragen, die Aufmerksamkeit auf Qualität zu lenken, aber Sie möchten nicht, dass es der Wertschöpfung im Wege steht. Ich persönlich bevorzuge es, Tests manuell auszuführen, da ich dadurch schnelleres Feedback zu dem Teil bekomme, an dem ich arbeite. Vor der Veröffentlichung werde ich mich auf die Qualität konzentrieren, bei der ich statische Analysen, Profiler und Tools zur Codeabdeckung verwende, um Problembereiche zu finden (wobei einige dieser Schritte Teil einer Testsuite vor der Veröffentlichung sind).

amon
quelle
3

Niemand hat Mutationstests erwähnt . Die Idee dahinter ist sehr praktisch und intuitiv.

Sie arbeiten, indem sie den Quellcode zufällig ändern (z. B. ">" in "<" umschalten) - daher Mutation - und prüfen, ob diese zufälligen Änderungen einen Test brechen.

Wenn dies nicht der Fall ist, ist entweder a) der betreffende Code möglicherweise nicht erforderlich, oder b) (wahrscheinlicher) dieser Code wird nicht durch einen Test abgedeckt, da das Brechen des Codes unentdeckt bleibt.

Konrad Morawski
quelle
1

Daten zur Codeabdeckung können natürlich automatisch abgerufen werden, aber aus Gründen, die andere bereits erläutert haben, sollten keine automatischen Entscheidungen auf dieser Grundlage getroffen werden. (Zu verschwommen, zu viel Raum für Fehler.)

Das nächstbeste ist jedoch ein etablierter Prozess, bei dem der aktuelle Status des Projekts in Bezug auf die Codeabdeckung regelmäßig von Menschen überprüft wird, möglicherweise mit täglichen Berichten, die im Posteingang des Projektmanagers eingehen.

In Unternehmensumgebungen wird dies mit Tools für die kontinuierliche Integration wie Hudson, Jenkins usw. erreicht. Diese Tools sind so konfiguriert, dass sie das gesamte Projekt regelmäßig aus dem Quellcode-Repository auschecken, erstellen, die Tests ausführen und Berichte erstellen. Natürlich können sie so konfiguriert werden, dass die Tests im Codeabdeckungsmodus ausgeführt werden und die Ergebnisse in diese Berichte aufgenommen werden.

Jetbrains macht auch TeamCity, das mir etwas leichter erscheint und für kleinere Software-Shops geeignet ist.

So erhält der Projektmanager regelmäßig Berichte zur Codeabdeckung, verwendet sein eigenes Urteilsvermögen und fungiert bei Bedarf als Durchsetzer.

Mike Nakis
quelle
1
Ich bin nicht der Wähler. Ich vermute, die Ablehnung war auf Ihren ersten Satz zurückzuführen. Die Codeabdeckung kann mit Sicherheit automatisch überprüft werden. Vielleicht sollten Sie diesen Satz ändern. Die vorliegende Frage ist, ob die Ergebnisse dieser automatisierten Codeabdeckungstests wiederum automatisiert verwendet werden sollten, beispielsweise in einem Git-Hook.
David Hammen
0

Die Codeabdeckung kann trotz allgemeiner Meinung automatisch überprüft werden. Die Purify-Toolsuite von Rational enthielt eine Codeabdeckungsfunktion. Es beruhte auf der Instrumentierung aller Funktionen (es arbeitete an den Binärdateien, aktualisierte jede Funktion oder rief mit etwas zusätzlichem Code auf), damit es Daten ausschreiben konnte, die dann dem Benutzer angezeigt wurden. Ziemlich coole Technologie, besonders zu der Zeit.

Aber selbst wenn wir uns wirklich sehr bemüht haben, eine 100% ige Abdeckung zu erreichen, haben wir nur ungefähr 70% geschafft! Es ist also eine sinnlose Übung.

In der Situation, Unit-Tests zu schreiben, halte ich eine 100% ige Abdeckung von Unit-Tests jedoch für noch sinnloser. Unit Test die Methoden, die Unit Test erfordern, nicht jeder Getter oder Setter! Beim Unit-Test sollte es darum gehen, die kniffligen Funktionen (oder Klassen TBH) zu überprüfen und nicht zu versuchen, in einem Prozess oder Tool, das schöne grüne Häkchen zeigt, Kästchen anzukreuzen.

gbjbaanb
quelle
0

Ich habe ein Werkzeug dafür gebaut

https://github.com/exussum12/coverageChecker

Verwenden von

bin/diffFilter --phpunit diff.txt clover.xml 70

Schlägt fehl, wenn weniger als 70% des Diff durch Unit-Tests abgedeckt werden.

Holen Sie sich das Diff durch.

git diff origin/master... > diff.txt

Angenommen, Sie haben vom Master verzweigt und werden wieder zum Master zusammengeführt

Ignorieren Sie das phpunit-Flag im Code, es ist wirklich nur eine Kleeprüfung, damit alles, was Klee ausgeben kann, es verwenden kann.

Wie andere Antworten vorgeschlagen haben, ist es keine gute Idee, dies auf 100% zu setzen

Exussum
quelle