Интеграция с системами отчётов

Механизм отчётов служит для представления результатов тестирования в презентационной форме. Он позволяет создавать пользовательские интеграции с инструментами построения отчётов (например Allure Report или ReportPortal), TMS, системами логирования или отправлять информацию о прохождении тестов через телекомуникационные средства связи.

В месте с дистрибутивом поставляется расширения console и allure добавляющие возможность выводить информацию о тестировании на консоль и интегрировать тесты с инструментом Allure Report.

Для описания собственного расширения отчётов необходимо создать отдельный python скрипт в каталоге extensions. В этот скрипт нужно поместить описание класса отчёта который должен наследоваться от класса BaseReport.

BaseReport

В классе отчёта можно переопределить следующие методы:

Метод Событие
start Запуск теста
start_step Переход на новый шаг
stop_step Завершение шага
sub_test Запуск субтеста
message Вывод сообщения
error Вывод сообщения об ошибке
warning Вывод предупреждения
action Выполнение команды (работает на любом действии в том числе и с ошибкой)
test_error Ошибка теста FAIL
fatal_error Ошибка теста FATAL
timeout_error Превышение времени выполнения теста
scip_test Пропуск теста
stop_test Остановка теста
close Завершение всех тестов

Каждый из этих методов будет вызван при наступлении соответствующего события. Все методы кроме close принимают данные о состоянии прохождения теста в виде экземпляра класса ReportData.

ReportData

Объект класса ReportData предоставляет информацию о выполнении текущего действия (т.е. команды с параметрами) и содержит следующие атрибуты:

Метод Тип данных Описание
is_parent bool Текущее действие выполняется в субтесте или субстепе
parent_uuid str Идентификатор родительского теста или субтеста
attempt int Количество перезапусков
repeated int Количество последовательных запусков
action_name str Наименование текущего действия
status str Статус текущего действия/шага
value str Значение возвращённое действием
debug [(str, str)] Список пар (“идентификатор”, “значение”)
thread_id str Идентификатор потока в котором запущен тест
timestamp timestamp Время запуска теста
time float Длительность выполнения текущей команды
timeout float Тайм-аут для текущего действия
url str Адрес текущей страницы
browser_name str Название браузера
browser_version str Версия браузера
step_title str Заголовок шага
step_uuid str Идентификатор шага
step_args dict Аргументы шага
step_time float Время выполнения текущего шага
step_parent_uuid str Идентификатор родительского шага
script str Идентификатор текущего скрипта
script_args dict Аргументы текущего скрипта
test_name str Наименование теста
test_title str Заголовок теста
test_id str Идентификатор теста
test_args dict Аргументы теста
test_status str Статус теста
test_parameters dict Параметры с которыми был запущен тест
test_retries int Максимальное количество перезапусков для теста
test_scope dict Пространство имён теста
file_line str Имя файла с исходным кодом теста и номер строки для текущего действия
arguments ([], {}) Позиционные и именованные аргументы для текущего действия
logs dict Словарь логов selenium-а (только для метода stop_test)
cookies dict Словарь cookies (только при ошибке)
links dict Словарь ссылок с дополнительной информацией о тестировании
message [str] Текст сообщений
description str Описание теста или шага
warning [str] Текст предупреждений
screenshot [bytes] Список скриншотов окна браузера или отдельных элементов в формате png
page_name str Заголовок страницы
page_source str Исходник страницы (только при ошибке)

Значение атрибута screenshot, cookies и page_source будет доступно только при вызове специальной команды или при возникновении ошибки.

Статусы действий и тестов

Для отслеживания состояния выполнения команд и тестов существуют статусы. Каждая команда завершается с тем или иным статусом. Если команда завершилась с ошибкой или предупреждением, то всему тесту будет присвоен соответствующий статус. Просмотреть статусы команд можно в режиме отладки.

Статусы команд:

Статус Описание
PASSED Команда успешно завершилась
SKIPPED Команда была пропущена
WARNING Статус теста был переведён в WARNING
FAILED Статус теста был переведён в FAILED
FATAL Статус теста был переведён в FATAL
TIMEOUT Статус теста был переведён в TIMEOUT
ERROR Статус теста был переведён в ERROR
SKIP_TEST Статус теста был переведён в SKIPPED
STOPPED Статус теста был переведён в STOPPED

Статусы тестов:

Статус Описание
UNKNOWN Тест не запускался
STARTED Тест был запущен, но не завершился
PASSED Тест успешно завершился
RETRO Тест успешно завершился, но не с первой попытки
WARNING Тест завершился с предупреждением
FAILED Тест завершился с ошибкой инварианта
FATAL Тест завершился с критической ошибкой
TIMEOUT Не хватило выделенного времени для завершения теста
ERROR Инфраструктурная ошибка
XFAILED Негативный тест завершился без ошибки
XPASSED Негативный тест завершился с ошибкой
SKIPPED Тест был пропущен
STOPPED Тест был остановлен

Пример

Ниже приведён пример простого расширения которое записывает в лог ход выполнения тестов:

#!/usr/bin/python3
# coding: utf-8

import os
import time

from report import BaseReport
from parameter import CONFIG, ItemConfig

ItemConfig.registrator(
    ItemConfig("TEXT_LOG_FILE", str, os.path.abspath("report.log"), "Путь к файлу лога")
)

class TextLog(BaseReport):
    def __init__(self):
        self._bufer     = {}
        self._log_file  = CONFIG.TEXT_LOG_FILE
        
        with open(self._log_file, "a") as log:
            log.write("%s\tSTART\t%s\n"%(str(time.time()), CONFIG.TEST_CASE_PATH))
    
    def _bufirezator(self, test_id, data):
        if not test_id in self._bufer:
            self._bufer[test_id] = []
        self._bufer[test_id].append(data)
    
    def start(self, report):
        if report.attempt > 0:
            title = "Перезапуск (%i/%i)" % (report.attempt, report.retries)
        if report.is_parent:
            title = "Субтест %s" % report.test_title
        else:
            title = "%s (%s)" % (report.test_title, report.browser_name)
        self._bufirezator(report.test_id, title)
        if report.test_description:
            self._bufirezator(report.test_id, report.test_description)
        if report.test_parameters:
            self._bufirezator(report.test_id, report.test_parameters)
    
    def start_step(self, report):
        self._bufirezator(report.test_id, "Запуск теста %s..." % report.step_title)
    
    def sub_test(self, report):
        self._bufirezator(report.test_id, "Запуск субтеста %s..." % report.message)
    
    def message(self, report):
        self._bufirezator(report.test_id, report.message)
    
    def error(self, report):
        self._bufirezator(report.test_id, "Ошибка в действии " + report.action_name)
    
    def warning(self, report):
        self._bufirezator(report.test_id, "Предупреждение: %s %s" % (report.action_name, report.warning))
    
    def test_error(self, report):
        self._bufirezator(report.test_id, "Ошибка в действии " + report.action_name)
    
    def fatal_error(self, report):
        self._bufirezator(report.test_id, "Фатальная ошибка в действии " + report.action_name)
    
    def timeout_error(self, report):
        self._bufirezator(report.test_id, "Превышение времени выполнения теста")
    
    def scip_test(self, report):
        self._bufirezator(report.test_id, "Тест пропущен!")
        self.stop_test(report)
    
    def stop_test(self, report):
        self._bufirezator(report.test_id, report.test_status)
        with open(self._log_file, "a") as log:
            for x in self._bufer.pop(report.test_id):
                log.write("\n"+x)
            log.write("\n\n")
    
    def close(self):
        with open(self._log_file, "a") as log:
            log.write("\nSTOP: %s"%str(time.time()))

Пользовательские атрибуты тестов

Если система отчётов оперирует данными которые не предусмотрены в стандартной модели описания теста, то их можно добавить через пользовательский атрибуты тестов, а потом получить через атрибут test_args объекта отчёта.

Например расширение report_allure добавляет следующие дополнительные атрибуты: epic, feature, story, issue, owner, tms_link.

Добавить пользовательский атрибут можно при помощи класса ItemTestConfig из модуля core_dsl.

ItemTestConfig(name, data_type, default [, description ] [, desirialize ] [, validator ])

Создаёт описание атрибута теста. Класс принимает следующие параметры:

Аргумент Описание
name Идентификатор параметра
data_type Тип данных
default Значение по умолчанию
description Краткое описание параметра По умолчанию - ""
desirialize Функция десериализатор По умолчанию - None
validator Функция валидатор По умолчанию - None

Параметр default может быть определён как функция. В этом случае ей будет перед запуском теста будет передаваться его объект.

Значение параметров desirialize и validator аналогично одноимённым параметров класса ItemConfig.

ItemTestConfig.registrator(описание параметра {описание параметра})

Регистрирует одно или несколько описаний атрибута теста.

Пример:

from core_dsl import ItemTestConfig

# Регистрируем атрибуты теста
ItemTestConfig.registrator(
    ItemTestConfig("owner",    str, "")
    , ItemTestConfig("tms_link", str, "")
)

# Теперь в тесте можно их определить
TEST["атрибуты"](
    ....
    , owner="admin"
    , tms_link="https://example.com/tms/123"
)

# И в объекте отчёта можно получить их значение

class Allure(BaseReport):
    def start(self, report):
        obj = TestResult(name=report.test_title, uuid=report.test_id, start=get_time(report))

        if report.test_args["owner"]:
            obj.labels.append(Label(name="owner", value=report.test_args["owner"]))

        if report.test_args["tms_link"]:
            obj.links.append(Link(LinkType.TEST_CASE, report.test_args["tms_link"]))