Este guia explora técnicas práticas para personalizar e aprimorar os relatórios gerados pelo pytest-html, focando na flexibilidade e na extração de informações úteis.
Configuração do Ambiente de Relatório
Todas as personalizações do relatório devem ser implementadas no arquivo conftest.py do seu projeto. As bibliotecas essenciais são:
from py.xml import html
import pytest
Gerenciamento da Seção de Informações do Ambiente
É possível adicionar ou remover itens da seção "Environment" do relatório. O exemplo abaixo demonstra a adição de um nome de projeto e a remoção de chaves como "JAVA_HOME" e "Plugins".
def pytest_configure(config):
config._metadata["Projeto"] = "Testes do Portal do Cliente"
config._metadata.pop("JAVA_HOME", None)
config._metadata.pop("Plugins", None)
Customização do Resumo
A seção "Summary" pode ser estendida com informações adicionais.
@pytest.mark.optionalhook
def pytest_html_results_summary(prefix):
prefix.append(html.p("Departamento: Engenharia de Qualidade"))
prefix.append(html.p("Responsável: Ana Silva"))
Ajustes na Tabela de Rseultados
Adicionando uma Coluna de Descrição
Para tornar o relatório mais informativo, insira uma coluna de descrição e remova a coluna de links, que pode não ser utilizada.
@pytest.mark.optionalhook
def pytest_html_results_table_header(cells):
cells.insert(1, html.th("Descrição"))
cells.pop(-1)
@pytest.mark.optionalhook
def pytest_html_results_table_row(report, cells):
cells.insert(1, html.td(report.description))
cells.pop(-1)
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
report.description = str(item.function.__doc__)
A propriedade report.description é preenchida com a docstring (__doc__) da função do caso de teste.
Controle de Exibição de Logs
Para reduzir a poluição visual, os logs podem ser ocultados para casos de teste que foram executados com sucesso.
@pytest.mark.optionalhook
def pytest_html_results_table_html(report, data):
if report.passed:
del data[:]
data.append(html.div("Teste aprovado. Nenhum log detalhado.", class_='empty log'))
Integração de Screenshots em Falhas
Durante testes de UI, capturar uma screenshot automaticamente quando um teste falha ajuda drasticamente na depuração. A imagem é embutida diretamente no relatório HTML.
Implementação do Mecanismo
O padrão utiliza uma variável global e fixtures para compartilhar a instância do driver do navegador.
shared_storage = {}
@pytest.fixture
def store_driver():
def _store(key, driver_instance):
shared_storage[key] = driver_instance
return _store
@pytest.fixture
def get_driver():
def _get(key):
return shared_storage.get(key)
return _get
O hook a seguir verifica se o teste falhou e gera a imagem codificada em base64.
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extras = getattr(report, 'extra', [])
if report.when in ('call', 'setup'):
if report.failed and not hasattr(report, 'wasxfail'):
screenshot_data = _generate_screenshot_base64()
image_html = (
'<div><img src="data:image/png;base64,%s" '
'alt="Falha" style="max-width:600px;" '
'onclick="window.open(this.src)"/></div>' % screenshot_data
)
extras.append(pytest_html.extras.html(image_html))
report.extra = extras
report.description = str(item.function.__doc__)
def _generate_screenshot_base64():
return shared_storage.get('driver').get_screenshot_as_base64()
Configurando o Driver no Teste
Utilize fixtures para injetar o driver e armazená-lo na variável compartilhada.
class TestUserFlow:
@pytest.fixture(autouse=True)
def setup_browser(self, store_driver):
self.driver = webdriver.Chrome()
store_driver('driver', self.driver)
yield
self.driver.quit()
def test_login_successful(self):
# Lógica do teste...
pass
Extração de Estatísticas de Execução
Para integrar com sistemas de notificação (como e-mails), é útil obter os totais de execução de forma programática.
import time
def pytest_terminal_summary(terminalreporter, exitstatus, config):
collected = terminalreporter._numcollected
passed = len([r for r in terminalreporter.stats.get('passed', []) if r.when != 'teardown'])
failed = len([r for r in terminalreporter.stats.get('failed', []) if r.when != 'teardown'])
errors = len([r for r in terminalreporter.stats.get('error', []) if r.when != 'teardown'])
skipped = len([r for r in terminalreporter.stats.get('skipped', []) if r.when != 'teardown'])
duration_sec = time.time() - terminalreporter._sessionstarttime
success_rate = (passed / collected) * 100 if collected > 0 else 0
summary_text = (
"Resumo da Execução:\n"
f"Total Coletado: {collected}\n"
f"Aprovados: {passed}\n"
f"Reprovados: {failed}\n"
f"Erros: {errors}\n"
f"Ignorados: {skipped}\n"
f"Taxa de Sucesso: {success_rate:.2f}%\n"
f"Duração Total: {duration_sec:.2f} segundos"
)
shared_storage['execution_summary'] = summary_text
O texto formatado fica disponível em shared_storage['execution_summary'] para ser utilizado em outros componentes, como o corpo de um e-mail.