Hallo Habr!
Ich arbeite an einem großen Integrationsprojekt (IBM WAS, WebSphere MQ, Oracle) und verpacke unser blutiges Unternehmen mit einem Netz von Funktionstests in JMeter, das auf einem Prüfstand ausgeführt wird und beim Aufruf von Jenkins nach der Bereitstellung des neuen Builds aufwacht. Als die Anzahl der Tests zunahm, stieß ich auf das Problem, die Testdokumentation auf dem neuesten Stand zu halten.
Der Testbaum selbst in JMeter ist im Wesentlichen ein Dokument - Trades unterteilen die Funktionalität in logische Blöcke, Controller enthalten Tests innerhalb der Trades und jeder Sampler innerhalb des Controllers ist ein separater Schritt. Die Hierarchie der Objekte ist klar nummeriert, mit Ausnahme von Serviceteilen wie Asserts, Timern und anderen Dingen, die aus Sicht der Geschäftslogik weniger interessant sind.
Das Ergebnis ist ein ziemlich genaues Bild:

Allerdings ist nicht jeder Manager bereit, JMeter zu Hause zu starten, um die Situation im Bereich der Qualitätssicherung zu überprüfen. In der Vergangenheit wird die gesamte Projektdokumentation bei Confluence verwaltet.
Ich war nicht bereit, die Beschreibung der Testfälle manuell auf die Confluence-Seite zu kopieren, nachdem ich sie in JMeter entwickelt hatte. Verzweifeltes Googeln ergab kein Ergebnis - ich fand keine vorgefertigte und einfache Lösung für den Export eines Baums von Objekten aus JMeter in Text ( falls es einen gibt, schreiben Sie bitte in den Kommentaren darüber, und ich werde Asche auf meinen Kopf aus dem Widget "Ich kann googeln" streuen ).
Nachdem ich in die JMX-Datei (die Standarderweiterung des JMeter- Testplans) geschaut hatte , stellte ich fest, dass alle Objekte, die mich interessieren, mit dem Attribut testname gekennzeichnet waren:
Beispiel für ein JMX-Dateischnitt<AuthManager guiclass="AuthPanel" testclass="AuthManager" testname="1.4.2 " enabled="true"> <collectionProp name="AuthManager.auth_list"> <elementProp name="" elementType="Authorization"> <stringProp name="Authorization.url">http://${ipKvp}:${portKvp}/TKVPImportTemporary</stringProp> <stringProp name="Authorization.username">${userKvp}</stringProp> <stringProp name="Authorization.password">${passKvp}</stringProp> <stringProp name="Authorization.domain">${domainKvp}</stringProp> <stringProp name="Authorization.realm"></stringProp> </elementProp> </collectionProp> <boolProp name="AuthManager.clearEachIteration">true</boolProp> </AuthManager>
Sie müssen nur noch einen Parser schreiben, der:
- Der gewünschte Text mit der Beschreibung des Schritts \ test \ group aus der JMX-Datei
- Wirft Zeilen mit der Beschreibung uninteressanter Objekte (behauptet, Timer usw.)
- Es wird alles in eine Datei geschrieben, so dass die Dokumentaktualisierung ein einsames Kopieren und Einfügen enthält
Absatz 1 wurde erfolgreich mit dem regulären Ausdruck behandelt:
(? <= testname = ") (. *) (? =")
Der Reflex, den beim Schreiben von Selektoren für Selentests erworbenen xpath nicht zu verwenden, ersparte mir die Verwendung des xpath-Selektors.
Da ich die Serviceobjekte im Baum nicht nummeriert habe, wurde Punkt 2 ohne Probleme in einer Schleife implementiert, in der:
- Ich bekomme das erste Zeichen der Zeichenfolge
- Ich bringe zu int
- Wenn erfolgreich, schreiben Sie eine Zeile in die Liste
- sonst ignorieren
try (BufferedReader br = new BufferedReader(new FileReader(JMX_FILE))) { String line; while ((line = br.readLine()) != null) { Matcher m1 = p.matcher(line); if (m1.find()) { try { Integer.parseInt(m1.group().substring(0, 1)); matchd.add(m1.group()); } catch (NumberFormatException e) { System.out.println(m1.group().substring(0, 1) + ": excluding non-number string"); } } } }
Und da die Datei nacheinander von oben nach unten verarbeitet wird + die Nummerierung der Objekte im Baum einer klaren Logik folgt, war es auch für Punkt 3 nicht erforderlich, etwas Schreckliches zu erfinden:
FileWriter writer = null; try { writer = new FileWriter(RESULT_FILE); for (String str : matchd) { writer.write(str + "\n"); } } finally { if (writer != null) { writer.close(); } }
Das Endergebnis passt in eine kleine Klasse (~ 50 Zeilen):
Quellcode import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class App { private static final String SAMPLER_NAME_REGEXP = "(?<=testname=\")(.*)(?=\" )"; private static final File JMX_FILE = new File("C:\\temp\\Test-plan.jmx"); private static final File RESULT_FILE = new File("C:\\temp\\output.txt"); public static void main(String[] args) throws IOException { Pattern p = Pattern.compile(SAMPLER_NAME_REGEXP); List<String> matchd = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader(JMX_FILE))) { String line; while ((line = br.readLine()) != null) { Matcher m1 = p.matcher(line); if (m1.find()) { try { Integer.parseInt(m1.group().substring(0, 1)); matchd.add(m1.group()); } catch (NumberFormatException e) { System.out.println(m1.group().substring(0, 1) + ": excluding non-number string"); } } } } if (RESULT_FILE.delete()) { System.out.println("Deleting previous result file"); } else { System.out.println("Creating new result file"); } FileWriter writer = null; try { writer = new FileWriter(RESULT_FILE); for (String str : matchd) { writer.write(str + "\n"); } } finally { if (writer != null) { writer.close(); } } } }
Als Experiment habe ich versucht, diesen Code direkt in den JMeter-Testplan zu integrieren, bin jedoch auf Probleme gestoßen, Generika und Importe falsch zu verstehen, und habe mich bisher entschlossen, den resultierenden Baumexporteur in IDEA anzurufen.
Pass auf deine Zeit auf. Und danke fürs Zuschauen.