Столкнулся с задачей модификации html-отчета при работе с pytest, в результате чего нашёл удобное для своей задачи решение, хочу им поделиться — возможно кому-то пригодится.

* Все картинки кликабельны
Структура проекта:
|- test.py ( Основной тестовый сценарий )
|- conftest.py ( Локальный плагин в котором реализуются hook-сценарии )
|- test-1.jpg ( Файл с картинкой )
|- test-2.jpg ( Файл с картинкой )
Создание новых столбцов выполняется через функции: pytest_html_results_table_header(cells) и pytest_html_results_table_row(report, cells):
Перехват выполнения теста и заполнения отчета по тесту, производится через функцию, hook:
def pytest_runtest_makereport(item, call) с декоратором: @pytest.hookimpl(hookwrapper=True).
Т.е. при выполнении тестового сценария будет выполняться функция pytest_runtest_makereport(item, call), которая в свою очередь:
Передача параметров в отчет осуществляется через глобальные переменные и атрибут функции __doc__, при выполнении каждого теста мы должны переопределять глобальные переменные.
Присваивать значения глобальным переменным будем через функцию:
Что бы автоматически сбрасывать значения глобальных переменных и не контролировать их в каждом тесте, используем фикстуру.
Для запуска теста, переходим в каталог с тестовым сценарием и выполняем команду:
В результате выполнения теста будет создан файл report.html.
В данном случае разбирался пример передачи параметров в отчет через глобальные переменные, для моей задачи это вполне приемлемо. Но использование глобальных переменных накладывает известные сложности и проблемы, в связи с этим будьте осторожны.
Надеюсь данная статья будет Вам полезна.

* Все картинки кликабельны
Для работы необходимы следующие компоненты:
* python3 ( установка Linux: apt-get install python3 ) — поддержка языка Python
* pip3 ( установка Linux: apt-get install python3-pip ) — менеджер пакетов для Python (можно использовать любой другой)
* pytest ( установка Linux: pip3 install pytest ) — framework для тестирования
* pytest-html ( установка Linux: pip3 install pytest-html ) — плагин pytest для генерации html-отчетов
* pip3 ( установка Linux: apt-get install python3-pip ) — менеджер пакетов для Python (можно использовать любой другой)
* pytest ( установка Linux: pip3 install pytest ) — framework для тестирования
* pytest-html ( установка Linux: pip3 install pytest-html ) — плагин pytest для генерации html-отчетов
Структура проекта:
|- test.py ( Основной тестовый сценарий )
|- conftest.py ( Локальный плагин в котором реализуются hook-сценарии )
|- test-1.jpg ( Файл с картинкой )
|- test-2.jpg ( Файл с картинкой )
Содержимое test.py:
import pytest import base64 # глобальные переменные log = { 'a':'none', 'b':'none', 'sum':'none' } log_html = 'none' log_img = 'none' log_img_url = 'none' # Функция переназначения глобальных переменных def set_global_var(a='none',b='none',sum_='none',html='none',img='none',img_url='none'): global log, log_html, log_img, log_img_url log = { 'a': a, 'b': b, 'sum': sum_ } log_html = html log_img = img log_img_url = img_url # Фикстура, которая автоматически, перед каждым тестом будет сбрасывать значения глобальных переменных @pytest.fixture(scope="function", autouse=True) def default_global_var(): set_global_var() def test_default(): '''Тест с параметрами по умолчанию''' assert True def test_1(): '''Первый тест''' # Расчет суммы a, b = 2, 2 sum_ = a + b # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_ ) # Проверка assert sum_ == 4 def test_2(): '''Второй тест''' # Расчет суммы a, b = 3, 3 sum_ = a + b # Добавляем html блок html = ''' <p> <table border="3" width="100%"> <tbody> <tr> <td bgcolor="#D3D3D3"><font color="black"><strong>a</strong></font></td> <td bgcolor="#D3D3D3"><font color="black"><strong>b</strong></font></td> <td bgcolor="#D3D3D3"><font color="black"><strong>sum</strong></font></td> </tr> <tr> <td><font color="black">{0}</font></td> <td><font color="black">{1}</font></td> <td><font color="black">{2}</font></td> </tr> </tbody> </table> </p> '''.format( a, b, sum_ ) # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, html = html ) # Проверка assert sum_ == 6 def test_3(): '''Третий тест''' # Расчет суммы a, b = 4, 4 sum_ = a + b # Добавляем картинку в формате base64 image = open('test-1.jpg', 'rb') image_read = image.read() img = base64.encodebytes( image_read ).decode("utf-8") # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, img = img ) # Проверка assert sum_ == 8 def test_4(): '''Четвертый тест''' # Расчет суммы a, b = 5, 5 sum_ = a + b # Добавляем картинку по url img_url = 'test-2.jpg' # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, img_url = img_url ) # Проверка assert sum_ == 10 def test_5(): '''Пятый тест''' # Расчет суммы a, b = 5, 1 sum_ = a + b # Добавляем html блок html = '<h1>Произвольный HTML-блок</h1>' # Добавляем картинку в формате base64 image = open('test-1.jpg', 'rb') image_read = image.read() img = base64.encodebytes( image_read ).decode("utf-8") # Добавляем картинку по url img_url = 'test-2.jpg' # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, html = html, img = img, img_url = img_url ) # Проверка print ('Дополнительная информация о выполнении теста...') assert sum_ == 6 # Запускаем серию тестов @pytest.mark.parametrize("test_a, test_b, test_sum", [(1,2,3), (2,3,5), (4,5,8)]) def test_6(test_a, test_b, test_sum): '''Серия шестого теста''' # Расчет суммы a, b = test_a, test_b sum_ = a + b # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_ ) # Проверка assert sum_ == test_sum
Содержимое conftest.py:
import pytest from py.xml import html # Создание дополнительных столбцов def pytest_html_results_table_header(cells): cells.insert(1, html.th('description')) # Заголовок 1-го столбца cells.insert(2, html.th('a')) # Заголовок 2-го столбца cells.insert(3, html.th('b')) # Заголовок 3-го столбца cells.insert(4, html.th('sum')) # Заголовок 4-го столбца cells.pop() def pytest_html_results_table_row(report, cells): cells.insert(1, html.td( report.description )) # Содержимое 1-го столбца для конкретного теста cells.insert(2, html.td( report.a )) # Содержимое 2-го столбца для конкретного теста cells.insert(3, html.td( report.b )) # Содержимое 3-го столбца для конкретного теста cells.insert(4, html.td( report.sum )) # Содержимое 4-го столбца для конкретного теста cells.pop() # hook для перехвата и модификации данных результатов тестов @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): pytest_html = item.config.pluginmanager.getplugin('html') outcome = yield report = outcome.get_result() # Добавление значений в таблицу - значения берем из атрибута __doc__ функции и глобальных переменных report.description = str( item.function.__doc__ ) report.a = str( item.module.log['a'] ) report.b = str( item.module.log['b'] ) report.sum = str( item.module.log['sum'] ) # Добавление html и image блоков в результат - значения берем из глобальных переменных extra = getattr(report, 'extra', []) if report.when == 'call': # вставляем html-блок if item.module.log_html != 'none': extra.append(pytest_html.extras.html( item.module.log_html )) # вставляем img-блок (картинка будет встроена в отчет) if item.module.log_img != 'none': extra.append(pytest_html.extras.image( item.module.log_img, mime_type='image/jpg', extension='jpg' )) # вставляем img-блок с url-ссылкой if item.module.log_img_url != 'none': extra.append(pytest_html.extras.image( item.module.log_img_url )) report.extra = extra
Модификация отчета производиться в файле conftest.py
Создание новых столбцов выполняется через функции: pytest_html_results_table_header(cells) и pytest_html_results_table_row(report, cells):
Код
# Создание дополнительных столбцов def pytest_html_results_table_header(cells): cells.insert(1, html.th('description')) # Заголовок 1-го столбца cells.insert(2, html.th('a')) # Заголовок 2-го столбца cells.insert(3, html.th('b')) # Заголовок 3-го столбца cells.insert(4, html.th('sum')) # Заголовок 4-го столбца cells.pop() def pytest_html_results_table_row(report, cells): cells.insert(1, html.td( report.description )) # Содержимое 1-го столбца для конкретного теста cells.insert(2, html.td( report.a )) # Содержимое 2-го столбца для конкретного теста cells.insert(3, html.td( report.b )) # Содержимое 3-го столбца для конкретного теста cells.insert(4, html.td( report.sum )) # Содержимое 4-го столбца для конкретного теста cells.pop()
- cells.insert( _number, html.th( _name )) — производится вставка столбца в таблицу под _number с содержимым _name
Перехват выполнения теста и заполнения отчета по тесту, производится через функцию, hook:
def pytest_runtest_makereport(item, call) с декоратором: @pytest.hookimpl(hookwrapper=True).
Код
# hook для перехвата и модификации данных результатов тестов @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): pytest_html = item.config.pluginmanager.getplugin('html') outcome = yield report = outcome.get_result() # Добавление значений в таблицу - значения берем из атрибута __doc__ функции и глобальных переменных report.description = str( item.function.__doc__ ) report.a = str( item.module.log['a'] ) report.b = str( item.module.log['b'] ) report.sum = str( item.module.log['sum'] ) # Добавление html и image блоков в результат - значения берем из глобальных переменных extra = getattr(report, 'extra', []) if report.when == 'call': # вставляем html-блок if item.module.log_html != 'none': extra.append(pytest_html.extras.html( item.module.log_html )) # вставляем img-блок (картинка будет встроена в отчет) if item.module.log_img != 'none': extra.append(pytest_html.extras.image( item.module.log_img, mime_type='image/jpg', extension='jpg' )) # вставляем img-блок с url-ссылкой if item.module.log_img_url != 'none': extra.append(pytest_html.extras.image( item.module.log_img_url )) report.extra = extra
Т.е. при выполнении тестового сценария будет выполняться функция pytest_runtest_makereport(item, call), которая в свою очередь:
- report.description = str( item.function.__doc__ ) — столбцу report.description будет присвоено значение атрибута __doc__ тестовой функции;
- report.a = str( item.module.log['a'] ) — столбцу report.a будет присвоено значение глобальной переменной log['a'];
- report.b = str( item.module.log['b'] ) — столбцу report.b будет присвоено значение глобальной переменной log['b'];
- report.sum = str( item.module.log['sum'] ) — столбцу report.sum будет присвоено значение глобальной переменной log['sum'];
- extra.append(pytest_html.extras.html( item.module.log_html )) — внутри отчета по тесту будет создан html-блок, значение которого будет взято из глобальной переменной log_html;
- extra.append(pytest_html.extras.image( item.module.log_img, mime_type='image/jpg', extension='jpg' )) — внутри отчета по тесту будет создан img-блок, значение которого будет взято из глобальной переменной log_img;
- extra.append(pytest_html.extras.image( item.module.log_img_url )) — внутри отчета по тесту будет создан img-блок, значение которого будет взято из глобальной переменной log_img_url.
Сами тесты располагаются в файле test.py.
Передача параметров в отчет осуществляется через глобальные переменные и атрибут функции __doc__, при выполнении каждого теста мы должны переопределять глобальные переменные.
Код
# глобальные переменные log = { 'a':'none', 'b':'none', 'sum':'none' } log_html = 'none' log_img = 'none' log_img_url = 'none'
Присваивать значения глобальным переменным будем через функцию:
Код
# Функция переназначения глобальных переменных def set_global_var(a='none',b='none',sum_='none',html='none',img='none',img_url='none'): global log, log_html, log_img, log_img_url log = { 'a': a, 'b': b, 'sum': sum_ } log_html = html log_img = img log_img_url = img_url
Что бы автоматически сбрасывать значения глобальных переменных и не контролировать их в каждом тесте, используем фикстуру.
Код
@pytest.fixture(scope="function", autouse=True) def default_global_var(): set_global_var()
Для запуска теста, переходим в каталог с тестовым сценарием и выполняем команду:
pytest test.py -v --html=report.html --self-contained-html
В результате выполнения теста будет создан файл report.html.
Результат выполнения тестов:
- test_default()
Тест с параметрами по умолчанию:
Кодdef test_default(): '''Тест с параметрами по умолчанию''' assert True

- test_1()
Тест присваивает значения глобальным переменным и проверяет результат суммирования.
Кодdef test_1(): '''Первый тест''' # Расчет суммы a, b = 2, 2 sum_ = a + b # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_ ) # Проверка assert sum_ == 4

- test_2()
Тест присваивает значения глобальным переменным (в том числе и для html-блока) и проверяет результат суммирования.
Кодdef test_2(): '''Второй тест''' # Расчет суммы a, b = 3, 3 sum_ = a + b # Добавляем html блок html = ''' <p> <table border="3" width="100%"> <tbody> <tr> <td bgcolor="#D3D3D3"><font color="black"><strong>a</strong></font></td> <td bgcolor="#D3D3D3"><font color="black"><strong>b</strong></font></td> <td bgcolor="#D3D3D3"><font color="black"><strong>sum</strong></font></td> </tr> <tr> <td><font color="black">{0}</font></td> <td><font color="black">{1}</font></td> <td><font color="black">{2}</font></td> </tr> </tbody> </table> </p> '''.format( a, b, sum_ ) # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, html = html ) # Проверка assert sum_ == 6

- test_3()
Тест присваивает значения глобальным переменным (в том числе и для img-блока) и проверяет результат суммирования.
Кодdef test_3(): '''Третий тест''' # Расчет суммы a, b = 4, 4 sum_ = a + b # Добавляем картинку в формате base64 image = open('test-1.jpg', 'rb') image_read = image.read() img = base64.encodebytes( image_read ).decode("utf-8") # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, img = img ) # Проверка assert sum_ == 8

Картинка полностью встроена в страницу:

- test_4()
Тест присваивает значения глобальным переменным (в том числе и для img-блока) и проверяет результат суммирования.
Кодdef test_4(): '''Четвертый тест''' # Расчет суммы a, b = 5, 5 sum_ = a + b # Добавляем картинку по url img_url = 'test-2.jpg' # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, img_url = img_url ) # Проверка assert sum_ == 10

Картинка встроена в страницу через гиперссылку:

- test_5()
Тест присваивает значения глобальным переменным (в том числе и для html-блока, img-блока) и проверяет результат суммирования.
Кодdef test_5(): '''Пятый тест''' # Расчет суммы a, b = 5, 1 sum_ = a + b # Добавляем html блок html = '<h1>Произвольный HTML-блок</h1>' # Добавляем картинку в формате base64 image = open('test-1.jpg', 'rb') image_read = image.read() img = base64.encodebytes( image_read ).decode("utf-8") # Добавляем картинку по url img_url = 'test-2.jpg' # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_, html = html, img = img, img_url = img_url ) # Проверка print ('Дополнительная информация о выполнении теста...') assert sum_ == 6

- test_6()
Серия тестов, которые присваивают значения глобальным переменным и проверяют результат суммирования.
Код# Запускаем серию тестов
@pytest.mark.parametrize("test_a, test_b, test_sum", [(1,2,3), (2,3,5), (4,5,8)]) def test_6(test_a, test_b, test_sum): '''Серия шестого теста''' # Расчет суммы a, b = test_a, test_b sum_ = a + b # Присваивание значений глобальным переменным set_global_var( a = a, b = b, sum_ = sum_ ) # Проверка assert sum_ == test_sum

P.S.:
В данном случае разбирался пример передачи параметров в отчет через глобальные переменные, для моей задачи это вполне приемлемо. Но использование глобальных переменных накладывает известные сложности и проблемы, в связи с этим будьте осторожны.
Надеюсь данная статья будет Вам полезна.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Была ли полезна Вам данная статья?
27.78%Да, искал решение5
61.11%Да, возможно в будущем буду использовать11
5.56%Нет, все об этом и так знают1
5.56%Другое1
Проголосовали 18 пользователей. Воздержались 2 пользователя.

