Ускоряем работу с тестовой документацией. Экспорт данных из Allure-отчета в Confluence

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Для того, чтобы ускорить тестирование крупных приложений, как правило, проверки вручную сочетают с автотестами. После их прогона SDET-специалисты разбирают успешные и «упавшие» тесты – их нужно проверить вручную и зафиксировать результаты, например, в Confluence.

Рассмотрим на примере, как можно ускорить экспорт этих данных, если вы работаете с некоммерческой версией Allure Framework. При использовании Allure-EE такие доработки не нужны – информацию по ручному прохождению кейсов можно хранить в самих отчетах.

В описанном ниже кейсе мы работали с Allure, поскольку этот инструмент использовался и на других подпроектах заказчика. При этом шапка таблицы создавалась из шаблона, а данные для каждого автотеста приходилось вручную копировать из Allure-отчета. При большом количестве упавших автотестов этот процесс становился трудоемким и занимал достаточно много времени. Мы решили автоматизировать его и разработали утилиту, с помощью которой создавалась таблица, уже заполненная необходимыми данными из Allure-отчета.

Задачи, которые нам требовалось решить:

  • Найти и структурировать необходимые данные из json-файлов, которые создает Allure.

  • Сгенерировать html-код таблицы, используя полученные ранее данные.

  • Создать таблицу в Confluence с помощью POST-запроса, в теле которого передать html-код таблицы.

Получение данных из json-файлов

Json-файлы с которыми мы будем работать, находятся в каталоге allure-report\data\test-cases

Для каждого автотеста создается отдельный json-файл с информацией по его прохождению. Это достаточно большой файл, который содержит все шаги автотеста, поэтому в статье мы не будем приводить его полностью. Нас интересуют такие параметры:

"status" : "failed”,
"name" : "Test_0001",

{
    "name" : "epic",
    "value" : "iOS"
},

{
    "name" : "feature",
    "value" : "Авторизация"
},

{
    "name" : "story",
    "value" : "Превышение попыток"
}

Эти данные будем размещать во 2 - 5 колонках таблицы, используя определенные цвета для заливки фона ячеек. Статус автотеста может иметь несколько значений: "failed" (выделяем красным), "unknown" (фиолетовый), "broken" (желтый), "skipped" (серый).

Создадим два класса с моделями. Для генерации моделей удобно использовать IntelliJ IDEA плагин RoboPOJOGenerator. Также обратимся к Lombok, чтобы не создавать методы геттеры.

import com.google.gson.annotations.SerializedName;
import lombok.Getter;

import java.util.List;

@Getter
public class TestCase {
   private String epic;
   private String feature;
   private String story;

   @SerializedName("name")
   private String name;

   @SerializedName("status")
   private String status;

   @SerializedName("labels")
   private List<LabelsItem> labels;

   public void initLabels() {
       if (labels != null) {
           for (LabelsItem labelsItem : labels) {
               switch (labelsItem.getName()) {
                   case "epic" : epic = labelsItem.getValue();
                       break;
                   case "feature" : feature = labelsItem.getValue();
                       break;
                   case "story" : story = labelsItem.getValue();
               }
           }
       }
   }
}


import com.google.gson.annotations.SerializedName;
import lombok.Getter;

@Getter
public class LabelsItem {

  @SerializedName("name")
  private String name;

  @SerializedName("value")
  private String value;
}

Создадим метод, который будет получать эти параметры из json-файла. Преобразовывать json в класс модели будем при помощи Gson.

/**
* Получает данные из json файла
*
* @param path путь к папке с файлами
* @return данные из json файла
*/
public static TestCase readTestCasesJson(String path) {
   try (Stream<String> stream = Files.lines(Paths.get(path))) {
       String text = stream.reduce("", String::concat);
       return new Gson().fromJson(text, TestCase.class);
   } catch (IOException e) {
       throw new RuntimeException(e.getMessage());
   }
}

Для получения списка всех json-файлов воспользуемся следующим методом:

/**
* Получает список названий файлов в папке
*
* @param path путь к папке с файлами
* @return список названий файлов
* @throws IOException ошибка доступа к данным
*/
public static List<Path> getFilesInFolder(String path) throws IOException {
   return Files.walk(Paths.get(path))
           .filter(Files::isRegularFile)
           .collect(Collectors.toList());
}

Теперь создадим метод, который будет собирать данные по всем упавшим тестам.

/**
* Получает данные по всем упавшим
*
* @param paths путь к папке с файлами
* @return список с данными по упавшим тестам
*/
public static List<TestCase> getTestCasesDataInFolder(List<Path> paths) {
   List<TestCase> testCases = new ArrayList<>();
   TestCase testCase;

   for (Path path : paths) {
       testCase = readTestCasesJson(path.toString());

       if (!testCase.getStatus().equals("passed") ) {
           testCase.initLabels();
           testCases.add(testCase);
       }
   }

   return testCases;
}

Генерация таблицы

Далее создадим метод, который будет получать информацию по автотестам для всех статусов, сортировать их и создавать таблицу в формате html.

/**
* Формирует html код таблицы
*
* @param testCasesPath путь к папке с json файлами отчета
* @param link ссылка на прогон в CI
* @param name название прогона
* @return строка с html кодом таблицы
* @throws IOException ошибка доступа к данным
*/
public static String createTableBody(String testCasesPath, String link, String name) throws IOException {

   List<Path> filesPath = getFilesInFolder(testCasesPath);
   List<TestCase> tests = getTestCasesDataInFolder(filesPath);

   List<TestCase> notPassed = tests.stream()
.sorted(Comparator.comparing(TestCase::getStatus)
.thenComparing(TestCase::getEpic, nullsFirst(naturalOrder()))
.thenComparing(TestCase::getFeature, nullsFirst(naturalOrder()))
.thenComparing(TestCase::getStory, nullsFirst(naturalOrder()))
.thenComparing(TestCase::getName, nullsFirst(naturalOrder()))).collect(Collectors.toList());
  
   String lines = createTableLines(notPassed);
  
   return (format(TABLE_HEADER, name, link, link) + lines + TABLE_TAIL).replace("\"", "\\\"");
}

Для формирования строк таблицы используем следующий метод:

/**
* Формирует строки таблицы в Confluence
*
* @param testsCases список упавших тестов
* @return html код строк таблицы
*/
private static String createTableLines(List<TestCase> testsCases) {
   StringBuilder result = new StringBuilder();
   String colorValue = "";

   for (TestCase test : testsCases) {
       colorValue = getColorValue(test.getStatus());

       result.append(
                   format(
                           TABLE_LINE,
                           ++testNumber,
                           Strings.isNotNullAndNotEmpty(test.getEpic()) ? test.getEpic() : " ",
                           Strings.isNotNullAndNotEmpty(test.getFeature()) ? test.getFeature() : " ",
                           Strings.isNotNullAndNotEmpty(test.getStory()) ? test.getStory() : " ",
                           colorValue,
                           getColorName(test.getStatus()),
                           colorValue,
                           test.getName()
                   )
       );
   }

   return result.toString();
}

Для создания html-кода таблицы проще всего скопировать код из шаблона в Confluence и подставить нужные значения в определенные места с помощью функции format().

Сначала формируем хедер таблицы. После поочередно добавляем строки. Последней добавляем строку с тегами, закрывающими таблицу. Таким образом, мы сформируем html-код таблицы, который передадим в теле запроса.

Создание таблицы с помощью POST-запроса

Для создания таблицы с помощью запросов используем документацию Confluence REST API.

Для начала создадим в Confluence пустую страницу, которую будем использовать для нашего генератора таблиц. Так нам будет удобно отправлять запросы к странице с определенным id (пусть это будет 123456789), который можно посмотреть в адресе самой страницы https://***.ru/pages/viewpage.action?pageId=123456789

Для того чтобы сохранить новую таблицу, нам нужно узнать текущую версию страницы. Для этого используем GET запрос https://***.ru/rest/api/content/123456789?expand=version

Для работы с api используем RestAssured https://rest-assured.io/

И создадим метод для получения номера следующей версии страницы

/**
* Получает версию таблицы в Confluence
*
* @param url адрес страницы
* @param login логин от учетки Confluence
* @param password пароль от учетки Confluence
* @return номер следующей версии страницы
*/
public static int getNextPageVersion(String url, String login, String password) {
   int result = RestAssured
           .given()
           .auth()
           .preemptive()
           .basic(login, password)
           .get(url)
           .jsonPath().get("version.number");

   return result + 1;
}

Для создания таблицы используем POST-запрос https://***.ru/rest/api/content/123456789/ с телом:

{
    "body": {
        "storage": {
                        "value": "наш html код таблицы",
            "representation": "storage"
        }
    },

    "type": "page",
    "title": "Генератор таблиц",

    "version": {
        "number": "100"
        }
}

Здесь мы указываем заголовок страницы, html-код самой таблицы и номер новой версии страницы. Так будет выглядеть метод:

/**
* Создает таблицу в Confluence
*
* @param url адрес страницы
* @param login логин от учетки Confluence
* @param password пароль от учетки Confluence
* @param table html код таблицы
* @param title Название страницы
* @param pageVersion Версия страницы
*/
public static void createConfluenceTable(String url, String login, String password, String table, String title, int pageVersion) {
   String TABLE_BODY = "{\n" +
           "    \"body\": {\n" +
           "        \"storage\": {\n" +
           "                        \"value\": \"%s\",\n" +
           "            \"representation\": \"storage\"\n" +
           "        }\n" +
           "    },\n" +
           "\n" +
           "    \"type\": \"page\",\n" +
           "    \"title\": \"%s\",\n" +
           "\n" +
           "    \"version\": {\n" +
           "        \"number\": \"%s\"\n" +
           "        }\n" +
           "}";

   RestAssured
           .given()
           .auth()
           .preemptive()
           .basic(login, password)
           .header("Content-Type", "application/json")
           .header("Connection", "keep-alive")
           .body(format(TABLE_BODY, table, title, pageVersion))
           .when()
           .put(url)
           .then()
           .statusCode(200);
}

И главный метод для выполнения программы:

import utils.RequestExecutor;
import java.io.IOException;
import java.util.Scanner;

import static utils.TableGenerator.confluenceTableBody;

public class App {

   public static void main(String[] args) throws IOException {
//Путь до папки с отчетом
       String PATH = "C:\\Users\\User\\Desktop\\allure-report\\data\\test-cases";
       	//Название страницы в Confluence
String TITLE = "Генератор таблиц iOS";
	//Название таблицы
String NAME = ": \"№1 Смоук 1.01\"";
	//Ссылка на отчет в CI
String LINK = "https://ci.***.ru/job/IOS_Autotest/100/allure/#behaviors";
       	//Запрос для получения номера текущей версии страницы в Confluence
String URL_GET = "https://***.ru/rest/api/content/123456789?expand=version";
       	//Запрос для создания таблицы в Confluence
String URL_POST = "https://***.ru/rest/api/content/123456789/";

       System.out.println("Для подключения к учетной записи Confluence");
       Scanner in = new Scanner(System.in);

       System.out.print("Введите логин: ");
       String login = in.nextLine();

       System.out.print("Введите пароль: ");
       String password = in.nextLine();

       RequestExecutor.createConfluenceTable(
               URL_POST,
               login,
               password,
               confluenceTableBody(PATH, LINK, NAME),
               TITLE,
               RequestExecutor.getNextPageVersion(URL_GET, login, password)
       );
   }
}

Заключение 

За счет того, что мы автоматизировали работу с отчетами, в нашем проекте удалось сделать тестирование быстрее и проще:

• QA-специалисты смогли оперативно приступать к разбору отчетов, не затрачивая время на ручное создание таблиц и перенос данных из Allure-отчета.

• Снизилась вероятность ошибок, которые могли возникнуть при ручном переносе данных из Allure-отчета в таблицу.

• Работать с данными в таблице стало удобнее. 

В рамках проекта тестирование проводили на трех платформах: web, Android и iOS. Генератором таблиц первоначально пользовались только мобильные команды, при этом веб-команда уже к следующему регрессу оценила их опыт и тоже начала пользоваться предложенным методом.

Полный код к статье доступен на GitHub.

Спасибо за внимание! Надеемся, что наш опыт был вам полезен.

Источник: https://habr.com/ru/company/simbirsoft/blog/585530/


Интересные статьи

Интересные статьи

Специально к старту курса о Data Science мы перевели статью о созданной исследователями из Массачусетского технологического института программе, занимающей около 50 строк...
В этой статье мы рассмотрим, как система управления 1С-Битрикс справляется с большими нагрузками. Данный вопрос особенно актуален сегодня, когда электронная торговля начинает конкурировать по обороту ...
В то время как коронавирус шагает по планете, на рынке ценных бумаг лидирует туалетная и целые страны закрывают на карантин, все больше компаний вынуждены переводить сотрудников на удаленную рабо...
Визуализация многомерных данных очень полезна для выявления их важных закономерностей и свойств. Для этой цели используются алгоритмы снижения размерности. Среди наиболее распространенных алгорит...
Михаил Коновалов, руководитель направления отдела сопровождения интеграционных проектов ИТ-дирекции МКБ День добрый, хабровчане! Цель Систематизированный подход к управлению загрузками. Мы...