it-swarm.com.de

Unerwartete Reduzierung der Codeabdeckung

Ich füge Unit-Tests zu einem vorhandenen Code hinzu, der ursprünglich überhaupt nicht getestet wurde.

Ich habe mein CI-System so eingerichtet, dass Builds fehlschlagen, bei denen der Prozentsatz der Codeabdeckung im Vergleich zum vorherigen Build reduziert ist - hauptsächlich, um einen Weg zur kontinuierlichen Verbesserung festzulegen.

Ich bin dann auf eine unerwartete und nervige (wenn auch mathematisch korrekte) Situation gestoßen, die sich auf Folgendes reduzieren lässt:

  • Vor dem Refactor:
    Methode 1: 100 Codezeilen, 10 abgedeckt -> 10% Abdeckung
    Methode 2: 20 Codezeilen, 15 abgedeckt -> 75% Abdeckung

    Gesamt: 25/120 -> ~ 20% Deckung

  • Nach dem Refactor:
    Methode 1: 100 Codezeilen, 10 abgedeckt -> 10% Abdeckung (unberührt)
    Methode 2: 5 Codezeilen, 5 abgedeckt -> 100% Abdeckung

    Gesamt: 15/105 -> ~ 14% Deckung

Obwohl IMO die Situation verbessert wurde, stimmt mein CI-System offensichtlich nicht überein.

Zugegeben, dies ist ein sehr esoterisches Problem und würde wahrscheinlich verschwinden, wenn der Großteil des Codes besser abgedeckt wird, aber ich würde mich über Erkenntnisse und Ideen (oder Tools/Konfigurationen) freuen, die es mir ermöglichen könnten, weiterhin einen "Verbesserungs" -Pfad für durchzusetzen Abdeckung mit meinem CI-System.

Die Umgebung ist Java, Jenkins, JaCoCo.

35
Jonathan Gross

Sie können den Effekt bis zu einem gewissen Grad abschwächen, indem Sie zulassen, dass sich die relative Codeabdeckung verringert, wenn sich auch die Gesamtzahl der nicht abgedeckten Zeilen verringert oder wenn sich die Gesamtzahl der Zeilen verringert, da dies ziemlich klare Anzeichen für ein Refactoring sind, das eine neue Basislinie festlegt für Ihre Abdeckungsmetriken.

In Ihrem Beispiel verringert sich die Gesamtzahl der nicht abgedeckten Zeilen von 95 auf 90 Zeilen und die Gesamtzahl der Zeilen von 120 auf 105. Dies sollte Ihnen genügend Sicherheit geben, dass die relative Abdeckung in dieser Situation ziemlich unwichtig ist. Wenn Sie jedoch neuen Code hinzufügen, spiegeln Ihre Metriken die Erwartung wider, dass der neue Code nicht weniger relativ abgedeckt wird als zuvor.

Randnotiz: Beachten Sie, dass keine dieser Metriken Aufschluss darüber gibt, ob die Tests die sinnvollsten Teile der Codebasis abdecken.

18
Doc Brown

Das Problem, das ich hier sehe, ist, dass Sie die Codeabdeckung gemacht haben ein Auslöser für einen Build-Fehler. Ich bin der Meinung, dass die Codeabdeckung routinemäßig überprüft werden sollte, aber wie Sie erfahren haben, können Sie Ihr Streben nach einer höheren Codeabdeckung vorübergehend reduzieren.

Im Allgemeinen Buildfehler sollten vorhersehbar sein. Die folgenden Ereignisse führen zu guten Buildfehlern:

  • Code wird nicht kompiliert
  • Tests werden nicht ausgeführt
  • Tests schlagen fehl
  • Post-Build-Verpackung schlägt fehl (d. H. Es können keine Container usw. hergestellt werden)
  • Pakete können nicht in Repositorys veröffentlicht werden

All dies sind Bestanden/Nicht Bestanden, quantitative Maßnahmen, sie funktionieren (Binärwert 1) oder sie funktionieren nicht (Binärwert 0).

Codequalität sollte überwacht werden weil sie qualitativ sind . HINWEIS: Prozentsätze sind ein qualitatives Maß, obwohl ihnen eine Zahl zugeordnet ist. Das Folgende sind qualitative Maßnahmen:

  • Zyklomatische Komplexität (eine Zahl, die einem qualitativen Konzept zugeordnet ist, d. H. Die Zahl hat eine qualitative Bedeutung)
  • Wartbarkeitsindex (eine Zahl, die einem qualitativen Konzept zugeordnet ist, d. H. Die Zahl hat eine qualitative Bedeutung)
  • Prozentsatz der abgedeckten Linien/Zweige/Methoden (qualitative Zusammenfassung der quantitativen Ergebnisse)
  • Statische Analyseergebnisse (keine Zahlen beteiligt)

Wie bei jedem Trend können Sie beim Blick auf frühere Releases vorübergehende Reduzierungen feststellen, während sich der Gesamttrend verbessert oder gleich bleibt (100% bleibt 100%). Wenn der langfristige Trend zu weniger getestetem oder wartbarem Code geht, haben Sie ein Teamproblem, das Sie angehen müssen. Wenn der langfristige Trend zu Code mit höherer Qualität basierend auf Ihren Messungen geht, erledigt das Team seine Arbeit.

47
Berin Loritsch

Haben Sie darüber nachgedacht, keine Kennzahlen für die Codeabdeckung zu verwenden?

Ich werde nicht argumentieren, dass Sie sich die Codeabdeckung nicht ansehen sollten. Es ist absolut. Es ist gut zu verfolgen, was vor und nach einem Build abgedeckt wurde. Stellen Sie außerdem sicher, dass Sie bei einer Änderung neue und geänderte Codezeilen abdecken (und je nach Umgebung möglicherweise Tools verwenden, die dies erleichtern).

Aber wie Sie gerade sehen, können Sie die abgedeckten Zeilen umgestalten und am Ende entfernen, sodass der Prozentsatz der abgedeckten Zeilen abnimmt. Ich stimme Ihrer Argumentation zu - die logische Abdeckung hat sich nicht geändert. Sie haben wahrscheinlich auch andere Aspekte Ihres Codes vereinfacht (ich wäre gespannt, wie sich beispielsweise verschiedene Komplexitätsmaße vor und nach Ihrem Refactoring geändert haben). Aber die Mathematik ist richtig - Ihr Prozentsatz der abgedeckten Zeilen ist tatsächlich gesunken.

Darüber hinaus sagt die Codeabdeckung absolut nichts über die Wirksamkeit oder Qualität der Tests aus. Sie sagen auch nichts über das Risiko hinter dem Teil des Codes. Es ist relativ einfach, Tests zu schreiben, die triviale Zeilen abdecken oder die einfachste Logik überprüfen, ohne viel Vertrauen in die Qualität des Systems zu haben.

37
Thomas Owens

Dies wird als Simpsons Paradoxon bezeichnet und ist ein bekanntes statistisches Problem bei Ihrem Ansatz.
Sie könnten sogar Fälle konstruieren, in denen Sie umgestalten und anschließend jede einzelne Methode eine höhere Abdeckung aufweist, die Gesamtabdeckung jedoch immer noch abnimmt .

Sie müssen andere Wege finden, um "Regressionen" des Typs zu erfassen, den Sie abfangen möchten, und nicht mit Gesamtprozentsätzen (obwohl mir der Ansatz beim Lesen gefallen hat).

14
Aganju

Während alle Antworten darauf hinweisen, dass die Codeabdeckung nicht als Qualitätsmetrik verwendet werden soll, hat keine die Frage wirklich beantwortet. Was tun mit reduzierter Deckung?
Die Antwort ist ganz einfach: Verwenden Sie absolute Zahlen, keine Prozent. Was wichtiger ist als der Prozentsatz der Abdeckung, ist die Anzahl der nicht getesteten Funktionen und die Anzahl der nicht getesteten Zweige. Es wäre auch großartig, eine Liste der nicht getesteten Funktionen und Zweige zu erhalten.

5
BЈовић

Eine Option, die dabei helfen könnte, ist der Wechsel von Leitungsabdeckung zu Zweigabdeckung. Ich sage 'könnte' helfen, weil Sie mit nicht abgedeckten Zweigen in eine ähnliche Situation geraten können, wenn Sie Zweige in einer Funktion reduzieren.

Insgesamt ist die Zweigabdeckung jedoch eine aussagekräftigere Metrik als die Leitungsabdeckung. Und ich gehe die Vermutung ein, dass Sie, wenn Sie eine lange Methode in eine kurze umgestalten, normalerweise Codezeilen reduzieren, als Sie verzweigen werden. Wenn es sich wirklich um fiesen Code handelt, werden Sie möglicherweise viele Zweige beschneiden. Selbst in diesem Fall denke ich, dass Sie immer noch besser dran sind, Zweige zu zählen.

2
JimmyJames

Was Sie tun, ist nicht falsch. Während die Codeabdeckung keine erstaunliche Metrik ist, ist sie eine nützliche Metrik. Es sagt Ihnen nicht, dass Ihr Code perfekt getestet ist. Es ist jedoch auch eine schlechte Idee, nicht den Standard zu haben, den Sie von Ihren Tests erwarten.

Eine Möglichkeit, dieses Problem zu lösen, besteht darin, das Limit einfach zu ändern, wenn Sie der Meinung sind, dass die Änderung gerechtfertigt ist. Der springende Punkt dieser Art von hartem Limit ist, Sie schnell zu benachrichtigen, wenn etwas Schlimmes passiert. In vielen Fällen ist diese Reduzierung unbeabsichtigt und muss durch die Einführung weiterer und besserer Tests behoben werden. Wenn Sie jedoch feststellen, dass der Fehler falsch positiv ist und Sie sich und Ihrem Team beweisen können, dass die Reduzierung erwartet wird, ist es vollkommen in Ordnung, das Limit zu reduzieren.

Eine andere Möglichkeit besteht darin, diese Reduzierung in einen "weichen" Fehler umzuwandeln. Zum Beispiel kann Jenkins ein "instabiles" Ergebnis eines Builds haben. In einem instabilen Build können die endgültigen Artefakte weiterhin verwendet werden, es kann jedoch zu einer gewissen Verschlechterung der Funktionalität kommen. So haben Sie Zeit zu entscheiden, was zu tun ist, wenn Tests erweitert oder Grenzwerte gesenkt werden müssen. Dies könnte auch mit zwei verschiedenen Builds implementiert werden: einem mit Build-Artefakten mit geringeren Überprüfungen und Einschränkungen, bei dessen Ausfall nicht einmal die Artefakte verwendet werden können. Und zweitens mit viel strengeren Grenzwerten, die fehlschlagen würden, wenn eine Ihrer Qualitätsmetriken nicht funktioniert.

2
Euphoric

Neben der Antwort von Berin Loritsch möchte ich auch eine Technik namens Mutation Testing hervorheben (siehe https://pitest.org/ für ein Java Tool)). .

Mutationstests bieten Metriken, anhand derer Codezeilen mutiert werden können, um sich anders zu verhalten, und nicht dazu, dass der Komponententest fehlschlägt.

In Ihrem Fall sollten sich die Mutationstest-Metriken verbessern, wenn das Refactoring gut durchgeführt wurde (ausgedrückt als Prozentsatz der Einheitentest-Linienabdeckung).

Dieser Link beschreibt einige (Java-bezogene) Mutationstest-Tools: https://pitest.org/Java_mutation_testing_systems/

1

Vor dem Refactor: 95 ungetestete Zeilen.

Nach dem Refactor: 90 ungetestete Zeilen.

Sie sollten immer überlegen, wie viele ungetestete Zeilen Sie haben!

Der Prozentsatz der Codeabdeckung ist nur dann ein gutes Maß dafür, wenn Ihr Code immer wächst, was unglücklich ist.

Anders ausgedrückt: Immer wenn Sie es schaffen, die Anzahl der Zeilen in Ihrem Code zu reduzieren, ohne Tests zu unterbrechen, haben Sie alle Rechte, den Prozentsatz der Codeabdeckung zu ignorieren!

1
Pedro A

Das Problem ist: Sie haben eine große Funktion mit sehr geringer Codeabdeckung. Um den Prozentsatz zu erhöhen, müssen Sie abgedeckte Codezeilen hinzufügen. Wenn Sie 20 Zeilen/10 abgedeckte hinzufügen, verbessert sich der Prozentsatz um mehr als 5 Zeilen/5 abgedeckte. Auch wenn die zweite höchstwahrscheinlich besser ist (es sei denn, die Geschwindigkeit war wichtig, und Ihre erste Funktion war "if (einfacher Fall) {einfacher Fall schnell behandeln} else {komplexer Fall langsam behandeln}" und die zweite war "alle Fälle langsam behandeln"). .

Wir kommen also zu dem Schluss: Sie haben eine messbare Sache, bei der eine Verbesserung der Messung die Qualität Ihres Codes nicht verbessert. Das bedeutet, dass Sie dies nur als Ziel verwenden sollten, wenn gleichzeitig der gesunde Menschenverstand angewendet wird. Aus einem Buch über "Motivation": Das Wichtigste ist, die Menschen so zu motivieren, dass das Befolgen der Motivation zu besseren Ergebnissen führt.

Stellen Sie sich vor, es gibt eine Funktion mit der Zeile "int x = 100;". Um die Codeabdeckung zu verbessern, ändere ich sie in "int x = 0; x ++; x ++; (100-mal wiederholen)". Anstelle einer abgedeckten Linie habe ich 101 abgedeckte Linien. Die gesamte Codeabdeckung steigt und ich bekomme einen Bonus.

0
gnasher729

Hier ist eine Lösung, bei der die Definition der Codeabdeckungsmetrik nicht geändert oder eine Ausnahme von der Richtlinie angefordert/gewährt wird. Obwohl die angegebene Richtlinie möglicherweise fehlerhaft ist, ist es möglicherweise besser, die Gesamtzahl der nicht abgedeckten Zeilen zu zählen oder eine Untergrenze pro Datei (z. B. 80%) zu erzwingen, anstatt Monotonie zu erfordern.

Verbessern Sie die Codeabdeckung von Methode 1 vor oder gleichzeitig mit der Umgestaltung von Methode 2.

Hier ist die aktuelle Situation in der Frage angegeben.

                    Old World                   New World

           covered  total   %           covered  total   %
 Method 1       10    100  10                10    100  10
 Method 2       15     20  75                 5      5 100
Aggregate       25    120  20.8              15    105  14.3

In der Neuen Welt muss die Gesamtzahl der abgedeckten Zeilen mindestens 22 Betragen, damit die Metrik nicht abnimmt. (Die Obergrenze von 105 * 25 / 120 Ist 22).

Sie müssen also nur Tests hinzufügen, die 7 weitere Zeilen der Datei abdecken, in denen Method 1 Und Method 2 Leben.

Diese Lösung hat den Nachteil, dass eine nicht verwandte Änderung in dasselbe Commit aufgenommen wird, um die Monotonie-Regel für die Codeabdeckung zu erfüllen. Es könnte sich dennoch lohnen, dies zu tun, wenn diese eine Änderung von Methode 2 nicht wichtig genug ist, um eine Änderung der Richtlinie zu rechtfertigen. Es kann sich auch lohnen, dies zu tun, wenn Sie diese Änderung zuerst vornehmen möchten, bevor Sie die Richtlinie ändern.

0
Gregory Nisbet