Как стать автором
Обновить

Экспорт дерева тестов из JMeter в текст

Тестирование IT-систем *Программирование *Java *Регулярные выражения *Apache *
Из песочницы

Привет, Хабр!


Работаю на большом интеграционном проекте (IBM WAS, WebSphere MQ, Oracle) и оплетаю наш кровавый энтерпрайз паутиной функциональных тестов в JMeter, который крутится на тестовом стенде и пробуждается по зову Jenkins после деплоя нового билда. По мере увеличения количества тестов столкнулся с проблемой поддержания тестовой документации в актуальном виде.


Само дерево тестов в JMeter по сути является документом — трэды разбивают функциональность на логические куски, внутри трэдов контроллеры содержат тесты, а каждый сэмплер внутри контроллера — отдельный шаг. Иерархия объектов четко пронумерована, за исключением служебных штук навроде ассершенов, таймеров и прочего менее интересного с точки зрения бизнес-логики.


В итоге получается достаточно аккуратная картинка:


image

Впрочем, не каждый менеджер готов запустить у себя JMeter для просмотра положения дел в области QA. Исторически сложилось, что вся документация проекта ведется в Confluence.
Я не был готов вручную копипастить описание тест-кейсов на страницу Confluence после разработки их в JMeter. Отчаянные гугления не дали результата — не обнаружил готового и легкого решения для экспорта дерева объектов из JMeter в текст (если таки есть, напиши о нем в комментарии, пожалуйста, а я посыплю голову пеплом из ачивки "умею в гугл").


Заглянув во внутренности JMX-файла (стандартное расширение тест-плана JMeter), обнаружил, что все интересные мне объекты отмечены атрибутом testname:


Образец кусочка 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>

Осталось дело за малым — написать парсер, который:


  1. Добудет желанный текст с описанием шага\теста\группы из JMX-файла
  2. Выкинет строки с описанием неинтересных объектов (ассершены, таймеры и прочее)
  3. Запишет всё по порядку в файл, чтобы актуализация документа включала один сиротливый копипаст

С пунктом1 успешно справилось регулярное выражение:


(?<=testname=\")(.*)(?=\" )

От использования xpath-селектора меня уберег рефлекс не использовать xpath, приобретенный в процессе написания селекторов для Selenium-тестов.


Так как я не нумеровал служебные объекты в дереве, пункт2 удалось реализовать без проблем в цикле, в котором:


  • достаю первый символ строки
  • привожу к int
  • в случае успеха записываю строку в список
  • иначе игнорирую
        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");
                    }
                }
            }
        }

И поскольку обработка файла идет подряд сверху вниз + нумерация объектов в дереве подчиняется четкой логике, ничего страшного для пункта3 также придумывать не пришлось:


        FileWriter writer = null;
        try {
            writer = new FileWriter(RESULT_FILE);
            for (String str : matchd) {
                writer.write(str + "\n");
            }
        } finally {
            if (writer != null) {
                writer.close();
            }
        }

Итоговый результат уместился в один маленький (~50 строк) класс:


Исходный код
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();
            }
        }
    }
}

В порядке эксперимента пробовал интегрировать этот код непосредственно в тест-план JMeter, но столкнулся с проблемами непонимания дженериков и импортов, и решил пока удовлетвориться вызовом полученного экспортера дерева в IDEA.


Берегите своё время. И спасибо за внимание.


image
Теги:
Хабы:
Всего голосов 8: ↑7 и ↓1 +6
Просмотры 2.4K
Комментарии Комментарии 10