Столкнулся с задачей модификации 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.:
В данном случае разбирался пример передачи параметров в отчет через глобальные переменные, для моей задачи это вполне приемлемо. Но использование глобальных переменных накладывает известные сложности и проблемы, в связи с этим будьте осторожны.
Надеюсь данная статья будет Вам полезна.
adante
А почему бы просто не прикрутить аллюр?