Bewertung der Testabdeckung eines Java-Projekts am Beispiel von Apache Ignite

Ich beteilige mich an der Entwicklung des Open-Source-Projekts Apache Ignite . Während ich an dem Projekt arbeitete, wurde es für mich interessant, die Testabdeckung zu bewerten, und genau das kam daraus.



Die Testabdeckung ist die beliebteste Messgröße für die Bewertung der Qualität von Produkttests.


Dies ist eine der wenigen Metriken, mit denen Sie Bereiche identifizieren können, die aufgrund des Risikos, einen Fehler zu verpassen, Aufmerksamkeit benötigen, und die Arbeit an Modulen oder Projektkomponenten priorisieren können.


Der einfachste Weg, um einen vollständigen Bericht über die Bewertung der Testabdeckung eines Java- Projekts zu erhalten, ist die Verwendung des in IntelliJ IDEA integrierten Abdeckungsläufers. Sie können mit wenigen Klicks eine Sammlung von Metriken konfigurieren und Tests mit anschließender Berichterstellung ausführen.


Testen im Apache Ignite-Projekt


Das Apache Ignite- Projekt verwendet zum Testen ein eigenes Testframework, das auf der Basis von JUnit 3 implementiert wurde. Zum Zeitpunkt des Schreibens enthält das Kernprojektmodul ~ 82.000 Tests, von denen die meisten Komponenten sind und das Auslösen eines Clusters von mehreren Knoten, einschließlich verschiedener JVMs, erfordern. bei gleichzeitiger Vorbereitung der Umwelt.


Es ist erwähnenswert, dass es keine leichte Aufgabe ist, die Funktionsfähigkeit einer so großen Regressionsbasis sicherzustellen. Die Community überwacht ständig den Status des Produkts und korrigiert Fehler, die im Rahmen der Initiative Make Teamcity Green Again gefunden wurden .


Mit den angegebenen Projektfunktionen können aus folgenden Gründen nicht alle Tests gleichzeitig in einer JVM ausgeführt werden:


  • möglicher Fehler OutOfMemoryError;
  • möglicher Absturz der JVM;
  • mögliche Deadlocks;
  • Unfähigkeit, den Test aufgrund eines nicht gestoppten Knotens im vorherigen Test zu starten;
  • Der Lauf dauert drei Tage auf einem Computer.

All dies macht es unmöglich, IntelliJ IDEA zu verwenden, um einen Bericht über alle Tests des Projekts zu erhalten, und erfordert einen speziellen Ansatz zur Lösung des Problems.


Vorbereitung und Durchführung einer Bewertung der Testabdeckung


Basierend auf der geleisteten Arbeit wurde der zuverlässigste Ansatz zur Erledigung der Aufgabe ausgewählt, der die folgenden Schritte enthält:


  1. Definieren einer Reihe von Testklassen;
  2. Ausführung für jede Testklasse:
    2.1. Starten und Ausführen einer Reihe von Klassentests in einer separaten JVM mit einem Watchdog-Timer, der den Thread bei Einfrierungen oder Problemen mit Tests beendet.
    2.2. Operationen zum Abrufen und Speichern von Testabdeckungsmetriken;
    2.3. Reinigen der Umwelt nach Abschluss der Tests;
  3. Zusammenführung aller in Absatz 2 erhaltenen Metriken;
  4. Vollständige Berichterstellung.

Es gibt viele Tools zur Bewertung der Testabdeckung, von denen die beliebtesten sind:



Ich werde nicht auf ihre Unterschiede eingehen. Hier wird eine visuelle Tabelle vorgestellt, in der die Funktionen von Tools zur Bewertung der Testabdeckung verglichen werden .


Um das Problem zu lösen, wurde die JaCoCo- Bibliothek ausgewählt, um die Lösung in TeamCity einbetten zu können , auf der die vorhandene Testinfrastruktur des Apache Ignite- Projekts basiert. TeamCity kann mit JaCoCo sofort arbeiten .


Um den beschriebenen Algorithmus zu automatisieren, wurden ein Bash-Skript und Maven verwendet . Die Konfiguration des Jacoco Maven-Plugins wird durch ein separates Maven- Profil in pom.xml implementiert.


Das Konfigurationsprofil des JaCoCo- Plugins ist unten angegeben und impliziert eine Trennung in zwei separate Starts:


  1. Führen Sie Tests mit dem verbundenen JaCoCo- Agenten ( Prepare -Agent ) aus, um Metriken für die Testabdeckung zu erfassen. Die Eigenschaft 'runDirectory' wird beim Start vom Skript übergeben, wodurch die Ergebnisse von Läufen isoliert gespeichert werden können.
  2. Führen Sie die Laufergebnisse ( Zusammenführen ) und die Berichterstellung ( Bericht ) zusammen.

Maven JaCoCo Konfiguration
<profile> <id>coverage</id> <properties> <argLine> -ea \ -server \ -Xms1g \ -Xmx6g \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:+AggressiveOpts \ -DIGNITE_UPDATE_NOTIFIER=false \ -DIGNITE_NO_DISCO_ORDER=true \ -DIGNITE_PERFORMANCE_SUGGESTIONS_DISABLED=true \ -DIGNITE_QUIET=false \ -Djava.net.preferIPv4Stack=true \ </argLine> <coverage.dataFile>${runDirectory}/coverage-reports/jacoco-ut.exec</coverage.dataFile> <coverage.outputDir>${runDirectory}/jacoco-ut</coverage.outputDir> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> </configuration> <executions> <execution> <id>default-test</id> <phase>test</phase> <goals> <goal>test</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.1</version> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <destFile>${coverage.dataFile}</destFile> </configuration> </execution> <execution> <id>post-merge</id> <phase>validate</phase> <goals> <goal>merge</goal> </goals> <configuration> <fileSets> <fileSet> <directory>${basedir}</directory> <includes> <include>results/*/coverage-reports/jacoco-ut.exec</include> </includes> </fileSet> </fileSets> <destFile>merged.exe</destFile> </configuration> </execution> <execution> <id>generate-report</id> <phase>validate</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${basedir}/merged.exe</dataFile> <outputDirectory>${basedir}/coverage-report</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> 

Unten finden Sie ein Skript, das die zuvor beschriebenen Schritte implementiert.


Kontrollieren Sie das Bash-Skript
 #!/bin/bash #        DEVNOTES.txt # #    : ignite/modules/core # #   : 'nohup ./coverage.sh >/dev/null 2>&1 &' SCRIPT_DIR=$(cd $(dirname "$0"); pwd) echo "***** ." echo "*****   ..." tests=() while IFS= read -r -d $'\0'; do tests+=("$REPLY") done < <(find $SCRIPT_DIR/src/test/java/org/apache/ignite -type f -name "*Test*" ! -name "*\$*" ! -name "*Abstract*" ! -name "*TestSuite*" -print0) testsCount=${#tests[@]} echo "*****   ="$testsCount idx=0 for path in ${tests[@]} do idx=$((idx+1)) echo "*****  "$idx"  "$testsCount echo "*****  : "$path filename=$(basename -- "$path") filename="${filename%.*}" echo "*****  : "$filename runDir=$SCRIPT_DIR"/results/"$filename mkdir -p $runDir if [ "$(ls -A $runDir)" ]; then continue fi echo "*****  ..." timeout 30m mvn -P surefire-fork-count-1,coverage test -Dmaven.main.skip=true -Dmaven.test.failure.ignore=true -Dtest=$filename -DfailIFNoTests=false -DrunDirectory=$runDir echo "*****  ..." pkill java done #      mvn -X -P surefire-fork-count-1,coverage validate echo "***** ." 

Die Ausführung aller Tests mit Abdeckungsbewertung dauerte ~ 50 Stunden auf einem dedizierten Server: 4 vCPU, 8RAM, 50 SSD, Ubuntu x64 16.04 .


Der beschriebene Ansatz kann leicht auf mehrere Stände parallelisiert werden, wenn Ressourcen verfügbar sind, wodurch sich die Zeit für die Ausführung und Bewertung der Testabdeckung erheblich verkürzt. Nach dem Einbetten dieser Lösung in TeamCity sollte die Bewertungszeit für die Testabdeckung etwa 2 Stunden betragen.


Ergebnisse


Nach den Ergebnissen des Berichts beträgt die Abdeckung der Projektanweisungen ~ 61%.


Abdeckung der Anweisungen für die Hauptkomponenten:


  • Cache - 66%
  • Entdeckung - 57%
  • Berechnen - 60%
  • Stream - 51%
  • Binär - 68%
  • Transaktionen - 71%

Nach der Analyse der Ergebnisse wurde deutlich, dass der gesamte Hot-Code sowie der Code zur Behebung typischer Probleme abgedeckt waren. Mit einem solchen Toolkit kann die Abdeckung auf seltene und atypische Situationen ausgedehnt werden, wodurch das Produkt noch zuverlässiger wird.


PS Vollständiger Bericht zur Überarbeitung .

Source: https://habr.com/ru/post/de413587/


All Articles