Как собирать статистику по pytest-прогонам в Test IT

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

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!


Как известно, основной проблемой в тестировании является отчетность по прогонам. Некоторые компании собирают данные в отдельном хранилище. Вместо того, чтобы вручную организовывать хранение, было решено сохранять их в Test IT. Такие данные как: исход, время выполнения и количество автоматизированных кейсов позволяют разделить тесты на выборки и дать оценку покрытия автотестами.


Это практично, поскольку в Test IT всё сделали до нас и не придется заново изобретать велосипед. Объединив Test IT и pytest мы сможем выборочно запускать авто тесты. В итоге должна получиться мастер система, из которой будут запускаться тестовые выборки и собираться статистика.


Несмотря на то, что существует множество СУП (Allure TestOps, TestLink и т.п.), решили остановится на Test IT, поскольку это быстро растущий проект по доступной цене с поддержкой на русском языке, а лицензия на TestRail уже закончилась.


Техническое задание


В рамках задачи следует:


  • Настроить проект в Test IT
  • Настроить прохождение pytest-тестов в GitLab CI
  • Написать плагин для pytest который:
    • создает автоматизированный кейс в Test IT
    • связывает автоматизированный и ручной кейс
    • запускает только те автоматизированные кейсы, которые были выбраны в прогоне
    • отмечает результаты в режиме реального времени

Замечания


  • Для работы с Test IT Swagger API следует создать API-secret-key

Реализация


1. Создание и связывание автоматизированных кейсов в Test IT


В Test IT существует два вида кейсов: ручной и автоматизированный



Для того чтобы в прогоне появилась кнопка Launch Autotests автоматизированный кейс должны быть создан и привязан к ручному



В таком случае напротив ручного кейса появится символ ракеты



Создание и связывание автоматизированного кейса происходит через endpoint AutoTests, в который следует передать два параметра:


  • testit_case_title — название автоматизированного кейса
  • testit_case_id — идентификатор ручного кейса для привязки

@pytest.mark.testit_case_id(29035)
@putest.mark.testit_case_title("Проверка первой успешной загрузки файла")
def test_download_first(test_it_configuration):
    """
        Автотест прилинкованный к ручному тесту
    """
    from pprint import pprint
    pprint(test_it_configuration)
    sleep(randint(a=0, b=8))

Для запуска GitLab CI агента через Test IT следует создать WebHook со следующими параметрами:



Параметры для GitLab CI:


  • URL — ссылка на trigger от GIT-проекта
  • ref — ветка, в которой запускаются тесты
  • token — токин от GitLab CI триггера

Параметры для запуска PYTEST:


  • TEST_IT — флаг сообщающий, что тесты запущены из Test IT
  • TESTIT_URL — ссылка на Test IT
  • API_KEY — ключ для Test IT Swagger API
  • PROJECT_ID — идентификатор проекта в котором вы работаете
  • TEST_IT_MODE — режим работы с Test IT

TEST_IT_MODE принимает следующие значения:


  • DELETE_AUTOTESTS
  • CREAT_AUTOTESTS
  • LINK_AUTOTESTS
  • RUN

Эти параметры будут переданы в GitLab CI и сохранены в переменных окружения агента



Тесты получают значения из переменных окружения агента при помощи модуля getenv


from os import getenv

class TestItParams:
    """
        Параметры из Test IT
    """
    is_test_it = eval(getenv('TEST_IT', 'False'))
    mode = getenv('TEST_IT_MODE', 'REGULAR')
    url = getenv('TESTIT_URL', 'http://tmslt-1.video.rt.ru/')
    key = getenv('API_KEY', None)
    project_id = getenv('PROJECT_ID', None)
    run_id = getenv('TEST_RUN_ID', None)
    configuration_id = getenv('CONFIG_ID', None)

Класс TestItParams следует сохранить в объекте session


def pytest_sessionstart(session):
    """
        SetUP тестовый сессии: собрать env-параметры из TestIT
    """

    session.test_it = TestItParams

Для создания автоматизированного кейса во время запуска pytest, нам нужно достать два параметра testit_case_title и testit_case_id.


Эти параметры можно получить из pytest_collection_modifyitems, поскольку он вызывается во время составления списка на запуск.


Для этого напишем plugin в файле conftest.py


def pytest_collection_modifyitems(session, items):
    """
        Перехват состояния перед публикацией списка кейсов
    """

    if session.test_it.is_test_it is True:
        print('Тесты запущены для TEST_IT')

        if 'DELETE_AUTOTESTS' in session.test_it.mode:
            delete_all_auto_test_from_project(pytest_session=session)
        if 'CREAT_AUTOTESTS' in session.test_it.mode:
            create_autotest_in_project(pytest_session=session, pytest_items=items)
        if 'LINK_AUTOTESTS' in session.test_it.mode:
            link_autotests_to_testcases(pytest_session=session, pytest_items=items)
        if 'RUN' in session.test_it.mode:
            select_autotest_from_testrun_only(pytest_session=session)
    else:
        print('\nПроходит штатный запуск')

Функции create_autotest_in_project и link_autotests_to_testcases это обычные API запросы через endpoint AutoTests.


Параметр test_it_mode используется для разделения задач: создание, привязка, удаление или запуск.


2. Выборочный запуск pytest-тестов


При создании тестового плана возникает потребность в запуске только выбранных автоматизированных кейсов.


Поскольку pytest ничего не знает о тестовом плане, то он должен запрашивать его самостоятельно.


Для этого по значению параметра TEST_RUN_ID через endpoint TestRunes мы получаем список тестов в текущем прогоне.



Далее выбираем только те тесты, которые есть в прогоне и удаляем остальные.


def pytest_collection_modifyitems(session, items):
    """
        Перехват состояния перед публикацией списка кейсов: создание, удаление, линковка, выборка
    """

    if session.test_it.is_test_it is True:
        print('Тесты запущены для TEST_IT')

        if 'DELETE_AUTOTESTS' in session.test_it.mode:
            delete_all_auto_test_from_project(pytest_session=session)
        if 'CREAT_AUTOTESTS' in session.test_it.mode:
            create_autotest_in_project(pytest_session=session, pytest_items=items)
        if 'LINK_AUTOTESTS' in session.test_it.mode:
            link_autotests_to_testcases(pytest_session=session, pytest_items=items)
        if 'RUN' in session.test_it.mode:
            select_autotest_from_testrun_only(pytest_session=session)
    else:
        print('\nПроходит штатный запуск')

Фильтрация происходит в объекте session.items в момент составления списка на запуск.


def select_autotest_from_testrun_only(pytest_session):
    """
        Выбрать для запуска только те кейсы, которые содержатся в тестовом прогоне
    """

    run_api = TestRunes(pytest_session.test_it.url, pytest_session.test_it.key)
    current_test_run = run_api.get_testrun_by_id(pytest_session.test_it.run_id)

    external_ids = []
    for case in current_test_run['testResults']:
        if case['startedOn'] is None:
            external_ids.append((case['autoTest']['externalId'],
                                 case['configuration']))

    edited_session_items = []
    for case in pytest_session.items:
        for external_id in external_ids:
            if case.name in external_id[0]:
                case.test_it_configuration = external_id[1]
                edited_session_items.append(case)
    pytest_session.items = edited_session_items

3. Возврат результата прохождения pytest-тестов


Каждый автоматизированный кейс должен получать результат прохождения в режиме реального времени а не постфактум.


Для возвращения статуса в реальном времени нам следует воспользоваться хуком pytest_runtest_makereport с декоратором @pytest.hookimpl(hookwrapper=True).


Он позволяет перехватывать выполнение ОДНОГО теста ДО и ПОСЛЕ прохождения.


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item):
    """
        Перехватывает состояние каждого теста во время: setup и done
    """

    outcome = yield

    if 'RUN' in item.session.test_it.mode:
        provide_test_results_into_run(pytest_session=item.session,
                                      pytest_outcome=outcome,
                                      pytest_item=item)

Этот хук возвращает объект outcome, в котором содержится информация о прогоне текущего теста.


Передача результатов прогона в Test IT происходит через через endpoint TestRunes.


def provide_test_results_into_run(pytest_session, pytest_outcome, pytest_item):
    """
        Предоставить результат выполнения теста в TestIT
    """
    test_run_api = TestRunes(pytest_session.test_it.url, pytest_session.test_it.key)
    res = pytest_outcome.get_result()
    if res.when == "call":
        autotest_id = pytest_item.name
        outcome = res.outcome
        duration = res.duration
        message = ''
        traceback = ''

        if res.outcome == 'failed':
            message = res.longrepr.reprcrash.message
            traceback = res.longreprtext

        test_run_api.set_auto_test_results_for_test_run(run_id=pytest_session.test_it.run_id,
                                                        autoTestExternalId=autotest_id,
                                                        outcome=humanize(outcome),
                                                        message=message,
                                                        traces=traceback,
                                                        duration=duration,
                                                        configurationId=pytest_item.test_it_configuration['id'])

Вывод


Объединив Test IT и pytest мы собрали мастер систему которая умеет:


  • запускать автотесты в GitLab CI
  • получать результат в режиме реального времени
  • собирать статистику по запускам
  • запускать выборки тестов

Test IT имеет красивый интерфейс, предоставляющий кольцевые-диаграммы, различные таблицы и графики. Можно узнать, в каком прогоне упал конкретный тест и сколько времени он выполнялся.


Демонстрация



Ссылки


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


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

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

История про конкурс разработки торговых роботов, ожидания и реальность участников, про важность фидбека и даже тема психологии по касательной прошла. Завариваем чаек, усаживаемся и обо всем по порядку...
В прошлой статье мы писали, что давно пора снова собрать русскоязычное сообщество rust-разработчиков, и поэтому в декабре проведем RustCon Russia. Так вот, при подготовке конференции мы поняли, что ес...
Ежегодно Pentestit выпускает уникальные лаборатории тестирования на проникновения. По своей сути, Test lab являются реальными копиями корпоративных сетей, которые содержа...
В 1С Битрикс есть специальные сущности под названием “Информационные блоки, сокращенно (инфоблоки)“, я думаю каждый с ними знаком, но не каждый понимает, что это такое и для чего они нужны
Лучше поздно, чем никогда. Или как мы чуть не допустили серьёзную ошибку, не имея поддержки обычных Dockerfiles для сборки образов приложения. Речь пойдёт про werf — GitOps-утилиту, котора...