Ist es sinnvoll, die bedingte Abdeckung für Java 8-Code zu messen?

19

Ich frage mich, ob das Messen der bedingten Code-Abdeckung mit aktuellen Java-Tools nicht überholt ist, seit Java 8 verfügbar ist. Mit Java 8 ist Optionalund Streamwir können oft vermeiden Code branches / Loops, die es einfach macht , zu bekommen sehr hohe bedingte Abdeckung ohne Prüfung aller möglichen Ausführungspfade. Vergleichen wir alten Java-Code mit Java-8-Code:

Vor Java 8:

public String getName(User user) {
    if (user != null) {
        if (user.getName() != null) {
            return user.getName();
        }
    }
    return "unknown";
}

Bei der obigen Methode gibt es 3 mögliche Ausführungspfade. Um 100% der bedingten Deckung zu erhalten, müssen 3 Einheitentests erstellt werden.

Java 8:

public String getName(User user) {
    return Optional.ofNullable(user)
                   .map(User::getName)
                   .orElse("unknown");
}

In diesem Fall sind die Zweige ausgeblendet und wir benötigen nur einen Test, um eine 100% ige Abdeckung zu erhalten. Dabei spielt es keine Rolle, welchen Fall wir testen. Obwohl es immer noch die gleichen 3 logischen Zweige gibt, die ich glaube, abgedeckt werden sollten. Ich denke, dass es die Statistik der bedingten Deckung heutzutage zu einem absoluten Vertrauensverlust macht.

Ist es sinnvoll, die bedingte Abdeckung für Java 8-Code zu messen? Gibt es andere Tools, die untersuchten Code erkennen?

Karol Lewandowski
quelle
5
Coverage-Metriken waren noch nie eine gute Methode, um festzustellen, ob Ihr Code gut getestet wurde, sondern lediglich, um festzustellen, was noch nicht getestet wurde. Eine gute Entwicklerin wird die verschiedenen Fälle in ihrem Kopf durchdenken und Tests für alle entwickeln - oder zumindest alles, was sie für wichtig hält.
kdgregory
3
Eine hohe bedingte Abdeckung bedeutet natürlich nicht, dass wir gute Tests haben, aber ich denke, dass es ein RIESIGER Vorteil ist zu wissen, welche Ausführungspfade aufgedeckt sind und worum es hauptsächlich geht. Ohne bedingte Abdeckung ist es viel schwieriger, ungetestete Szenarien zu erkennen. In Bezug auf Pfade: [user: null], [user: notnull, user.name:null], [user: notnull, user.name:notnull]. Was ich vermisse
Karol Lewandowski
6
Was ist der Vertrag von getName? Es scheint, dass wenn usernull ist, es "unbekannt" zurückgeben sollte. Wenn usernicht null und user.getName()null ist, sollte "unknown" zurückgegeben werden. Wenn usernicht null und user.getName()nicht null ist, sollte es das zurückgeben. Sie würden diese drei Fälle in einem Unit-Test testen, denn getNamedarum geht es in dem Vertrag . Sie scheinen es rückwärts zu machen. Sie möchten die Zweige nicht sehen und die Tests nicht danach schreiben, Sie möchten Ihre Tests gemäß Ihrem Vertrag schreiben und sicherstellen, dass der Vertrag erfüllt ist. Dann haben Sie eine gute Abdeckung.
Vincent Savard
1
Wiederum sage ich nicht, dass die Berichterstattung beweist, dass mein Code perfekt getestet wurde, aber es war ein EXTREM wertvolles Tool, das mir zeigt, was ich nicht sicher getestet habe. Ich denke, dass das Testen von Verträgen untrennbar mit dem Testen von Ausführungspfaden verbunden ist (Ihr Beispiel ist außergewöhnlich, da es implizite Sprachmechanismen beinhaltet). Wenn Sie den Pfad nicht getestet haben, haben Sie den Vertrag nicht vollständig getestet oder der Vertrag ist nicht vollständig definiert.
Karol Lewandowski
2
Ich werde meinen früheren Punkt wiederholen: Das ist immer der Fall, es sei denn, Sie beschränken sich nur auf grundlegende Sprachfunktionen und rufen niemals eine Funktion auf, die nicht instrumentiert wurde. Das heißt, keine Bibliotheken von Drittanbietern und keine Verwendung des SDK.
kdgregory

Antworten:

4

Gibt es Tools zum Messen logischer Zweige, die in Java 8 erstellt werden können?

Mir sind keine bekannt. Ich habe versucht, den Code, den Sie haben, über JaCoCo (auch bekannt als EclEmma) auszuführen, nur um sicherzugehen, aber es werden 0 Zweige in der OptionalVersion angezeigt . Ich kenne keine Methode zum Konfigurieren, um etwas anderes zu sagen. Wenn Sie es so konfigurieren, dass es auch JDK-Dateien enthält, werden theoretisch Verzweigungen angezeigt Optional, aber ich halte es für dumm, mit der Überprüfung des JDK-Codes zu beginnen. Sie müssen nur davon ausgehen, dass es richtig ist.

Ich denke, das Kernproblem ist jedoch, dass die zusätzlichen Zweige, die Sie vor Java 8 hatten, in gewisser Weise künstlich erstellte Zweige waren. Dass sie in Java 8 nicht mehr existieren, bedeutet nur, dass Sie jetzt das richtige Werkzeug für den Job haben (in diesem Fall Optional). In Code vor Java 8 mussten Sie zusätzliche Komponententests schreiben, damit Sie sicher sein können, dass sich jeder Codezweig in akzeptabler Weise verhält - und dies wird ein bisschen wichtiger in Codeabschnitten, die nicht so trivial sind wie der User/ getNameBeispiel.

Im Java 8-Code vertrauen Sie stattdessen auf das JDK, dass der Code ordnungsgemäß funktioniert. So wie es ist, sollten Sie diese OptionalZeile genauso behandeln wie Code Coverage Tools: 3 Zeilen mit 0 Zweigen. Dass es im Code unten noch andere Zeilen und Zweige gibt, haben Sie bisher noch nicht beachtet, aber es gab sie jedes Mal, wenn Sie so etwas wie ein ArrayListoder verwendet haben HashMap.

Shaz
quelle
2
"Dass sie in Java 8 nicht mehr existieren ..." - Ich kann dem nicht zustimmen, Java 8 ist abwärtskompatibel ifund nullimmer noch ein Teil der Sprache ;-) Es ist immer noch möglich, Code auf alte Weise zu schreiben und zu übergeben nullBenutzer oder Benutzer mit nullNamen. Ihre Tests sollten lediglich beweisen, dass der Vertrag erfüllt ist, unabhängig davon, wie die Methode implementiert ist. Der Punkt ist, dass es kein Tool gibt, mit dem Sie feststellen können, ob Sie den Vertrag vollständig getestet haben.
Karol Lewandowski
1
@KarolLewandowski Ich denke, was Shaz sagt, ist, dass Optionalman sie nicht mehr testen muss , wenn man der Funktionsweise (und verwandten Methoden) vertraut . Nicht so, wie Sie getestet haben if-else: Jeder ifwar ein potenzielles Minenfeld. Optionalund ähnliche funktionale Redewendungen sind bereits kodiert und werden Sie garantiert nicht stören, sodass im Grunde genommen ein "Zweig" verschwunden ist.
Andres F.
1
@AndresF. Ich glaube nicht, dass Karol vorschlägt, dass wir testen Optional. Wie er sagte, sollten wir logischerweise immer noch testen, ob getName()verschiedene mögliche Eingaben in der von uns beabsichtigten Weise verarbeitet werden, unabhängig von ihrer Implementierung. Es ist schwieriger, dies zu bestimmen, ohne dass die Codeabdeckungswerkzeuge wie vor JDK8 helfen.
Mike Partridge
1
@MikePartridge Ja, aber der Punkt ist, dass dies nicht über die Zweigabdeckung erfolgt. Beim Schreiben ist eine Zweigabdeckung erforderlich, if-elseda jedes dieser Konstrukte vollständig ad-hoc ist. Im Gegensatz dazu Optional, orElse, mapusw, sind alle bereits getestet. Die Zweige "verschwinden", wenn Sie mächtigere Redewendungen verwenden.
Andres F.