Bonjour, Habr!
Je travaille sur un grand projet d'intégration (IBM WAS, WebSphere MQ, Oracle) et envelopper notre entreprise sanglante avec un web de tests fonctionnels dans JMeter, qui s'exécute sur un banc de test et se réveille à l'appel de Jenkins après le déploiement de la nouvelle version. À mesure que le nombre de tests augmentait, j'ai rencontré le problème de la mise à jour de la documentation des tests.
L'arbre de test lui-même dans JMeter est essentiellement un document - les métiers divisent la fonctionnalité en blocs logiques, les contrôleurs contiennent des tests à l'intérieur des métiers et chaque échantillonneur à l'intérieur du contrôleur est une étape distincte. La hiérarchie des objets est clairement numérotée, à l'exception des éléments de service tels que les assertions, les minuteurs et d'autres choses moins intéressantes du point de vue de la logique métier.
Le résultat est une image assez précise:

Cependant, tous les managers ne sont pas prêts à lancer JMeter chez eux pour voir la situation dans le domaine de l'assurance qualité. Historiquement, toute la documentation du projet est conservée chez Confluence.
Je n'étais pas prêt à copier manuellement la description des cas de test sur la page Confluence après les avoir développés dans JMeter. Une recherche désespérée n'a pas donné de résultat - je n'ai pas trouvé de solution prête à l'emploi et facile pour exporter un arbre d'objets de JMeter vers du texte ( s'il y en a un, écrivez à ce sujet dans les commentaires, s'il vous plaît, et je vais saupoudrer de la cendre sur ma tête à partir du widget `` Je peux google '' ).
Après avoir regardé à l'intérieur du fichier JMX (l'extension standard du plan de test JMeter), j'ai trouvé que tous les objets qui m'intéressaient étaient marqués avec l'attribut testname :
Exemple de tranche de fichier JMX<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>
Il ne reste plus qu'à écrire un analyseur, qui:
- Le texte souhaité avec la description de l'étape \ test \ group du fichier JMX
- Lance des lignes avec la description d'objets inintéressants (affirmés, minuteries, etc.)
- Il écrira tout dans un fichier afin que la mise à jour du document comprenne un copier-coller solitaire
Le paragraphe 1 a été traité avec succès par l'expression régulière:
(? <= testname = \ ") (. *) (? = \")
Le réflexe de ne pas utiliser le xpath acquis lors de l'écriture des sélecteurs pour les tests Selenium m'a évité d'utiliser le sélecteur xpath.
Comme je n'ai pas numéroté les objets de service dans l'arborescence, l'élément 2 a été implémenté sans problème dans une boucle dans laquelle:
- J'obtiens le premier caractère de la chaîne
- Je porte à int
- en cas de succès, écrivez une ligne dans la liste
- sinon ignorer
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"); } } } }
Et comme le fichier est traité successivement de haut en bas + la numérotation des objets dans l'arborescence suit une logique claire, il n'y avait pas non plus besoin d'inventer quelque chose de terrible pour le point 3:
FileWriter writer = null; try { writer = new FileWriter(RESULT_FILE); for (String str : matchd) { writer.write(str + "\n"); } } finally { if (writer != null) { writer.close(); } }
Le résultat final tient dans une petite classe (~ 50 lignes):
Code source 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(); } } } }
À titre expérimental, j'ai essayé d'intégrer ce code directement dans le plan de test JMeter, mais j'ai rencontré des problèmes de malentendu sur les génériques et les importations, et jusqu'à présent, j'ai décidé de me contenter d'appeler l'exportateur d'arbres résultant dans IDEA.
Prenez soin de votre temps. Et merci d'avoir regardé.