Интеграция с системами отчётов
Механизм отчётов служит для представления результатов тестирования в презентационной форме. Он позволяет создавать пользовательские интеграции с инструментами построения отчётов (например 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 | Статус текущего действия | 
| step_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 | [(str, bytes)] | Список пар: (“заголовок”, скриншот). Скриншиты в виде байт в формате png | 
| page_name | str | Заголовок страницы | 
| page_source | str | Исходник страницы (только при ошибке) | 
| device | (str, str, 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"]))Telegramm группа
Политика конфиденциальности
Персональные данные
