Testen Sie dies: Wie bestimmen wir, welche Tests bei Pull-Request-Prüfungen ausgeführt werden sollen?

Hallo Habr! Ich heiße Egor Danilenko. Ich entwickle eine digitale Plattform für das Corporate Internet Banking von Sberbank Business Online und möchte Ihnen heute über das von uns angewandte CI-Entwicklungsverfahren berichten.

Wie kommen Entwickleränderungen zu einer Infusion in den Release-Zweig? Der Entwickler nimmt Änderungen lokal vor und drängt in unser Versionskontrollsystem. Wir verwenden Bitbucket mit dem Plugin eines Autors (wir haben hier früher über dieses Plugin geschrieben). Bei diesen Änderungen wird die Assembly gestartet und die Tests werden verfolgt (Einheit, Integration, Funktion). Wenn die Assembly nicht fehlgeschlagen ist und alle Tests erfolgreich bestanden wurden, sowie nach einer erfolgreichen Überprüfung, wird die Pull-Anforderung in den Hauptzweig gegossen.

Aber im Laufe der Zeit ist die Anzahl der Teams gewachsen. Die Anzahl der Tests ist proportional gewachsen. Wir haben verstanden, dass eine solche Anzahl von Teams das Auftreten des Problems der langsamen Pull-Request-Check beschleunigen und es unmöglich werden würde, ein Produkt zu entwickeln. Derzeit haben wir ungefähr 40 Teams. Zusammen mit neuen Funktionen bringen sie neue Tests mit sich, die auch bei Pull-Anforderungen ausgeführt werden müssen.

Wir dachten, es wäre cool, wenn wir wüssten, welche Tests zum Ändern eines bestimmten Codeteils ausgeführt werden müssen.

Und so haben wir dieses Problem gelöst.

Erklärung des Problems


Es gibt ein Projekt mit Tests, und wir möchten bestimmen, welche Tests ausgeführt werden müssen, wenn eine bestimmte Datei „berührt“ wird.

Wir alle kennen die EclEmma JaCoCo-Codeabdeckungsbibliothek. Wir haben es als Grundlage genommen.

Ein bisschen über JaCoCo


JaCoCo ist eine Bibliothek zum Messen der Codeabdeckung mit Tests. Die Arbeit basiert auf der Analyse von Code-Bytes. Der Agent sammelt Ausführungsinformationen und lädt sie auf Anforderung oder beim Herunterfahren der JVM hoch.

Es gibt drei Arten der Datenerfassung:

  1. Dateisystem: Nach dem Stoppen der JVM werden Daten in eine Datei geschrieben.
  2. TCP-Socket-Server: Sie können externe Tools mit der JVM verbinden und Daten über den Socket empfangen.
  3. TCP Socket Client: Beim Start stellt der JaCoCo-Agent eine Verbindung zu einem bestimmten TCP-Endpunkt her.

Wir haben die zweite Option gewählt.

Lösung


Es ist erforderlich, die Fähigkeit zu realisieren, Anwendungen und Tests selbst mit dem JaCoCo-Agenten auszuführen.

Zunächst erweitern wir die Möglichkeit, Tests mit dem JaCoCo-Agenten durchzuführen.

Java Agent kann gestartet werden:

-javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2] 

Fügen Sie unserem Projekt eine Abhängigkeit hinzu:

 dependencies { compile 'org.jacoco:org.jacoco.agent:0.8.0' } 

Wir müssen nur mit dem Agenten beginnen, um Statistiken zu erfassen. Daher fügen wir gradle.properties das withJacoco-Flag mit dem Standardwert false hinzu. Wir geben auch das Verzeichnis an, in dem Statistiken, Adresse und Port erfasst werden.

Fügen Sie der Teststartaufgabe das Argument jvm mit dem Agenten hinzu:

 if (withJacoco.toBoolean()) { … jvmArgs "-javaagent:${tempPath}=${jacocoArgs.join(',')}".toString() } 

Nach jedem erfolgreichen Abschluss des Tests müssen wir nun Statistiken mit JaCoCo sammeln. Schreiben Sie dazu den TestNG-Listener.

 public class JacocoCoverageTestNGListener implements ITestListener { private static final IntegrationTestsCoverageReporter reporter = new IntegrationTestsCoverageReporter(); private static final String TEST_NAME_PATTERN = "%s.%s"; @Override public void onTestStart(ITestResult result) { reporter.resetCoverageDumpers(String.format(TEST_NAME_PATTERN, result.getInstanceName(), result.getMethod().getMethodName())); } @Override public void onTestSuccess(ITestResult result) { reporter.report(String.format(TEST_NAME_PATTERN, result.getInstanceName(), result.getMethod().getMethodName())); } } 

Fügen Sie testng.xml einen Listener hinzu und kommentieren Sie ihn aus, da wir ihn in einem normalen Testlauf nicht benötigen.

Jetzt haben wir die Möglichkeit, Tests mit dem JaCoCo-Agenten durchzuführen, wobei jede erfolgreiche Teststatistik gesammelt wird.

Ein bisschen mehr darüber, wie Reporter implementiert werden, um Statistiken zu sammeln.
Während der Reporterinitialisierung wird eine Verbindung zu Agenten hergestellt, ein Verzeichnis erstellt, in dem Statistiken gespeichert und Statistiken gesammelt werden.

Fügen Sie die Berichtsmethode hinzu:

 public void report(String test) { reportClassFiles(test); reportResources(test); } 

Die reportClassFile-Methode erstellt den Ordner jvm im Statistikverzeichnis, in dem die von Klassendateien gesammelten Statistiken gespeichert werden.

Die reportResources-Methode erstellt den Ressourcenordner, in dem die gesammelten Statistiken zu Ressourcen (für alle Nicht-Klassendateien) gespeichert werden.

Der Bericht enthält die gesamte Logik zum Herstellen einer Verbindung zu einem Agenten, zum Lesen von Daten aus einem Socket und zum Schreiben in eine Datei. Implementiert von von JaCoCo bereitgestellten Tools wie org.jacoco.core.runtime.RemoteControlReader / RemoteControlWriter.

Die Funktionen reportClassFiles und reportResources verwenden die generische Funktion dumpToFile.

 public void dumpToFile(File file) { try (Writer fileWriter = new BufferedWriter(new FileWriter(file))) { for (RemoteControlReader remoteControlReader : remoteControlReaders) { remoteControleReader.setExecutionDataVisitor(new IExecutionDataVisitor() { @Override public void visitClassExecution(ExecutionData data) { if (data.hasHits()) { String name = data.getName(); try { fileWriter.write(name); fileWriter.write('\n'); } catch (IOException e) { throw new RuntimeException(e); } } } }); } } } 

Das Ergebnis der Funktion ist eine Datei mit einer Reihe von Klassen / Ressourcen, die von diesem Test betroffen sind.

Nachdem wir alle Tests ausgeführt haben, haben wir ein Verzeichnis mit Statistiken zu Klassendateien und Ressourcen.

Es bleibt eine Pipeline für die tägliche Statistikerfassung zu schreiben und den Pipeline-Start von Pull-Request-Prüfungen zu ergänzen.

Wir sind nicht an den Phasen der Projektzusammenstellung interessiert, werden jedoch die Phase für die Veröffentlichung von Statistiken genauer betrachten.

 stage('Agregate and parse result') { def inverterInJenkins = downloadMavenDependency( url: NEXUS_RELEASE_REPOSITORY, group: '', name: 'coverage-inverter', version: '0', type: 'jar', mavenHome: wsp ) dir('coverage-mapping') { gitFullCheckoutRef '', '', 'coverage-mapping', "refs/heads/${params.targetBranch}-integration-tests" sh 'rm -rf *' } sh "ls -lRa ..//out/coverage/" def inverter = wsp + inverterInJenkins.substring(wsp.length()) sh "java -jar ${inverter} " + "-d ..//out/coverage/jvm " + "-o coverage-mapping//jvm " + "-i coverage-config/jvm-include " + "-e coverage-config/jvm-exclude" sh "java -jar ${inverter} " + "-d ..//out/coverage/resources " + "-o coverage-mapping//resources " + "-i coverage-config/resources-include " + "-e coverage-config/resources-exclude" gitPush '', '', 'coverage-mapping', "${params.targetBranch}-integration-tests" } 

Bei der Coverage-Zuordnung müssen wir den Dateinamen und darin eine Liste der Tests speichern, die ausgeführt werden müssen. Da das Ergebnis der Statistiksammlung der Name des Tests ist, in dem eine Reihe von Klassen und Ressourcen gespeichert sind, müssen wir das Ganze invertieren und unnötige Daten (Klassen aus Bibliotheken von Drittanbietern) ausschließen.

Wir invertieren unsere Statistiken und verschieben sie in unser Repository.

Statistiken werden jede Nacht gesammelt. Es wird in einem separaten Repository für jeden Release-Zweig gespeichert.

Bingo!

Wenn wir jetzt die Tests ausführen, müssen wir nur die geänderte Datei finden und die Tests bestimmen, die ausgeführt werden müssen.

Die Probleme, auf die wir gestoßen sind:

  • Da JaCoCo nur mit Bytecode arbeitet, ist es unmöglich, Statistiken zu Dateien wie .xml, .gradle, .sql aus der Box zu sammeln. Deshalb mussten wir unsere Entscheidungen „beschleunigen“.
  • Ständige Überwachung der Relevanz der Statistik und der Häufigkeit der Assembly. Wenn die nächtliche Assembly aus irgendeinem Grund fehlgeschlagen ist, wird die Statistik von gestern zur Überprüfung bei Pull-Anforderungen verwendet.

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


All Articles