Приветствую, меня зовут Илья, и я QA-голик.

Но сегодня речь не обо мне, как долго я этим занимаюсь, а только лишь об одном инструменте, с которым меня связывают лет пять работы. Речь пойдёт о TestRails. Я понимаю, что инструмент не новый, но всё-таки он ещё является достаточно популярным инструментом для ручного, и, что реже и больнее - автоматизированного тестирования. По-хорошему, поделиться своими наработками можно было раньше, но главное - это осознать такую необходимость, не правда ли?

Итак, для тех, кто когда-либо работал с TestRails, не секрет, что его удобство в качестве инструмента для отслеживания\управления автоматизацией достаточно сомнительно. Но, как это часто бывает - у вас уже есть солидная тестовая модель, тысяча ручных тестов, которые вы начали покрывать автотестами, например на условном pytest/selenium. А по какой-то причине, например, по желанию менеджеров, вам нужно собрать  в “одном инструменте тестирования”.  Как это сделать?

Всё зависит от вашего фреймворка, который вы используете. Самый быстрый ответ - это использовать утилиту trcli от Gurock и отсылать результаты с помощью неё. Есть альтернативные подходы - добавить метки или какой-то мапинг в докcтрингах теста с полем TestRails и дёргать API. В-общем, разбирайтесь сами. Здесь и далее мой опыт и моё решение всех возникших за пять лет задач.

Отправляем результаты автотестов в TestRails

Итак, попробовав trcli, без восторга, я обнаружил, что из коробки она работает не так, как я ожидаю. Не буду вдаваться в подробности, что API иногда подвисает, выдаёт результаты постранично и прочая… С другой стороны, мне не хотелось использовать что-то стороннее и далёкое от подхода оригинального Gurock. И решил написать свой “репортер” с тестировщицами и питонячим подходом. 

Итак, берём и ставим пакет:

 pip install testrail-api-reporter

Всё, что нужно - это как и в оригинале добавить поле automation_id к тест-кейсам в проекте(ах) TestRail, а сами тесты запустить с ключом junitxml, например так:

pytest --junitxml "junit-report.xml" "./tests"

После выполнения тестов pytest создать xml-репорт с результатами выполнения тестов. Теперь нужно его прочитать и отправить в TestRails:

url=https://your_tr.testrail.io`
email=your@email.com
password=your_password
project_number=42
test_suite_number=66
api=TestRailResultsReporter(url=url, email=email, password=password, project_id=project_id,
                            suite_id=test_suite_id, xml_report='junit-report.xml')
# А дальше просто вызываем
api.send_results()

В результате результаты будут добавлены, и создан тест-ран вроде:
AT run 2022-09-01T20:25:51

Естественно, если в TestRail существует такой тест, в котором заполнено поле automation_id, и его формат верный, вроде путь.к.тестовому_файлу.тестовый_класс.тест, то результаты будут добавлены для него, в противном случае - будут созданы недостающие тесты в папке pytest.

Вообще репорт можно настроить под ваши нужды, передав параметры в send_results()

  • title - целиком заменит имя тест-рана.

  • environment - окружение, будет добавлено в конце имени рана, например AT run 2022-09-01T20:25:51 on ENV.

  • timestamp - тайм-код, будет заменён код из xml-репорта.

  • run_id - id существующего тест-рана, если указан, то результаты будут добавлены к нему, новый тест-ран не будет создан.

  • close_run - если True, то любой тест-ран будет закрыт, по умолчанию True.

  • run_name - если вы не знаете run_id вы можете найти тест-ран по его имени, и тогда параметр run_id будет проигнорирован, если даже был указан.

  • delete_old_run - если тест-ран с указанным id или именем существовал ранее, то будет удалён, если True.

Также, не обязательно создавать отдельный reporter, если вы захотите отправить репорт для другого suite, например, просто вызовите api.method_name(), а именно:

set_project_id(project_id) - change project id;

set_suite_id(suite_id) - поменять id suite;

set_xml_filename(xml_filename) - изменить путь к репорту по умолчанию;

set_at_report_section(section_name) - изменить папку по умолчанию, где будут созданы недостающие тесты.

Отчёт по автоматизированному покрытию

Итак, с передачей результатов в TestRail разобрались, а что, если мы хотим собрать метрики, которых нет в TestRail, например по покрытию автотестами нашей тестовой модели? О прогрессе автоматизации и прочем? Наш менеджмент живёт в Confluence, и, почему бы не обновлять красивые графики прямо там? Что ж, для этого тоже есть решение!

# Создадим Confluence Reporter
confluence_reporter = ConfluenceReporter(username='Liberator', password='NoWar', url="https://my.confluence.com", confluence_page="1234")

# Теперь давайте создадим сразу несколько репортов!
confluence_reporter.generate_report(reports=automation_distribution, cases=area_distribution, values=priority_distribution, type_platforms=my_platforms, automation_platforms=my_automation_platforms)

# Ну или каждый репорт по отдельности
confluence_reporter.history_type_chart(type_platforms=my_platforms)  # график исторического покрытия автоматизации, разбитой по платформам
confluence_reporter.history_state_chart(automation_platforms=my_automation_platforms)  # график исторического покрытия автотестами по атрибуту
confluence_reporter.test_case_area_distribution(cases=area_distribution)  # график распределения тестов по платформам (круговая диаграмма)
confluence_reporter.test_case_priority_distribution(values=priority_distribution)  # график разбиения тестов по приоритету (круговая диаграмма)
confluence_reporter.automation_state(reports=automation_distribution)  # график автоматизированного покрытия по палтформам (столбчатая диаграмма)

Окей, тут, возможно нужны пояснения что откуда берётся. Конечно, данные берутся из TestRail. Данные можно получить так:

 testrails_adapter = ATCoverageReporter(url=tr_url, email=tr_client_email, password=tr_client_password,
                                 project=tr_default_project, priority=4, type_platforms=my_platforms,
                                 automation_platforms=automation_platforms)

# Теперь получим результаты по каждому типу
values = tr_reporter.test_case_by_priority()
cases = tr_reporter.test_case_by_type()
reports = tr_reporter.automation_state_report() 

Ну ладно, но всё равно непонятно, что такое “платформы”? Платформы - это участки (папки TestRail), или даже их объединение\пересечения, из которых мы берём данные. То есть, У нас могут быть общие тесты для функциональности нашего продукта, так и разные, специфичные, для каждой из платформ (например мобильные\десктоп браузеры).

# Вам нужно указать верхню секцию(и) (папку(и), где хранятся тесты для каждой из ваших платформ, репортер пройдётся рекурсивно и соберёт все тесты во вложенных папках.
# Также вам нужно указать имя поля, по которому будет выбираться принадлежность к автоматизации, по умолчанию используется ‘internal_name’, а именно ‘type_id'
# По умолчанию это значения "Automated", "Functional", "Other", и прочая

automation_platforms = (
    {'name': 'Desktop Chrome', 'internal_name': 'type_id', 'sections': [4242, 1111]},
    {'name': 'Desktop Firefox', 'internal_name': 'custom_firefox', 'sections': [2424]})

# Если вам не нужно собирать данные по автоматизации, вы можете использовать эти платформы без указания поля:
type_platforms = (
    {'name': 'UI', 'sections': [6969, 8888]},
    {'name': 'API', 'sections': [9696]})

Надеюсь, теперь должно стать понятнее. Но, возможно, вы не используете Confluence? Тогда просто отрисуйте графики в виде картинок!

plotly_reporter = PlotlyReporter(type_platforms=type_platforms)
plotly_reporter.draw_test_case_by_priority(filename='stacked_bar_chart.png', values=values)
plotly_reporter.draw_test_case_by_area(filename='pie_chart1.png', cases=cases)
plotly_reporter.draw_automation_state_report(filename="pie_chart2.png", reports=reports)
plotly_reporter.draw_history_type_chart(filename="line_stacked_chart.png")

for item in automation_platforms:
    plotly_reporter.draw_history_state_chart(chart_name=item['name'])

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

Ладно-ладно, не вручную же посылать нашему менеджменту картинки, правда? Для этого у меня тоже есть решение!

Например, мы можем послать репорт в виде e-mail:

chart_drawings = ['report_chart.png', 'path/to/more_graphics.png']
chart_captions = ['Priority distribution', 'AT coverage']
emailer = EmailSender(email="my_personal@email.com",
                      password="my_secure_password",
                      server_smtp="smtp.email_server.com",
                      server_port=587)
emailer.send_message(files=chart_drawings, captions=chart_captions, recipients=['buddy@email.com', 'boss@email.com'])

Правда, если вы используете GMail, вам нужно получить oauth токен и использовать его. Тогда EmailSender будет инициализирован так:

 emailer = EmailSender(email="my_personal@gmail.com",
                      gmail_token="token.json")

Кажется, что емейл - это немного архаизм? Ну тогда давайте пошлём репорт в Slack! Только не забудьте настроить токен!

slack_sender = SlackSender(hook_url='https://hooks.slack.com/services/{your}/{api}/{key}')
slack_sender.send_message(files=chart_drawings, captions=chart_captions)

Не забудь про бэкап!

Пока мы резвились с TestRail, не могли не возникнуть вопросы - а мы такую работу проделали, а что если результаты и наши тесты потеряются. Можно их как-то сохранить. Конечно, вы можете воспользоваться платными бэкапами. Но дорого? Не даёт руководитель денег, говорит, раз ты переписал trcli, давай и это сделай.

tc_backup = TCBackup(tr_url, tr_email, tr_password, test_rails_suite=3)
tc_backup.backup()  # Так мы получим backup.xml файл со всеми тестами
tc_backup.get_archive_backup(suffix='')  # А так мы получим backup.zip архив с тестами

Теперь осталось все эти тесты сохранить где-то в облаке? Давайте сохраним их в облаке GoogleDrive!

# Для начала создайте Google токен, для этого посетите:
# https://console.developers.google.com/apis/credentials?pli=1
# Пройдите: Create Credentials => OAuth client ID => TV and limited Input Devices и получите client_id и client_secret

# Теперь используйте их чтобы инициализировать загрузчик с параметрами: google_id = client_id и google_secret = client_secret
gdrive = GoogleDriveUploader(google_id=client_id, google_secret=client_secret)

# При первом запуске вас спросят ввести ваш user_code от аккаунта пользователя и активировать API token, 
# Но если у вас уже есть токены доступа, то сделайте следующее:
gdrive = GoogleDriveUploader(google_id=client_id, google_secret=client_secret, google_api_refresh_token=refresh_token)

# А теперь вы можете загрузить любой факт, но по умолчанию нам же нужно загрузить на заархивированный бэкап с тестами backup.zip
gdrive.upload(filename='backup.zip', mime_type=‘application/zip')

Что может быть не так?

Скорее всего, при использовании plotly вам нужно будет отдельно поставить orca:

 npm install -g electron orca

Обратите внимание, что Slack не поддерживает загрузку изображений, а принимает только ссылки на них. Чтобы изображения использовать в Slack, вы можете их загрузить быстро скажем на https://freeimage.host . Например так (утилита включена в пакет):

image_uploaded = upload_image(filename=chart_drawings[0], api_token=YOUR_SECRET_TOKEN)

# Достаём URL изображения
image_url = image_uploaded['image']

# Или его thumbnail
image_thumb = image_uploaded[‘thumb']

Заключение

На этом всё. Если этот материал был вам полезен, то не забудьте поставить лайк этому посту, написать комментарий, а так же, если воодушевитесь - поделиться монеткой:

https://www.donationalerts.com/r/rocketsciencegeek

Cсылка на GitHub:
https://github.com/wwakabobik/testrail_api_reporter

Ссылка на  pypi:
https://pypi.org/project/testrail-api-reporter/

Комментарии (0)