A criação de relatórios de testes visualmente atraentes e informativos é crucial para a comunicação eficiente dentro de equipes de desenvolvimento. Ao integrar o framework unittest, testes orientados a dados (DDT), manipulação de planilhas Excel, templates Jinja2 e geração HTML, é possível construir uma solução robusta que automatiza tanto a execução quanto a apresentação dos resultados.
Arquitetura do Fluxo de Trabalho O processo central envolve abstrair a lógica de teste para arquivos de dados externos, permitindo que analistas ou testadores definam casos sem escrever código Python. O framework utiliza o método discover() do unittest para localizar suítes de teste e executa-as gerando artefatos de saída. Em seguida, os resultados são processados por um template engine (Jinja2) para produzir documentos HTML dinâmicos. Por fim, esses relatórios podem ser distribuídos automaticamente via SMTP, Webhooks (DingTalk/WeCom) ou outras interfaces de notificação.
Implementação de Decors de Dados (Custom DDT) Para manter a compatibilidade com o padrão unittest enquanto se injeta dados dinamicamente, podemos criar decoradores personalizados que anexam metadados às funções de teste. Abaixo, apresentaoms uma implementação refatorada que utiliza YAML como fonte de dados alternativa e mantém a estrutura original de injeção de parâmetros:
import yaml
def inject_test_data(data_source):
"""
Decorator to attach external data to a test function.
This allows the test runner to iterate over datasets without modifying code logic.
:param data_source: A list of dictionaries or a file path for YAML loading.
:return: The decorated function with attached attributes.
"""
def decorator(func):
# If input is a string, assume it's a file path and load content
if isinstance(data_source, str):
try:
with open(data_source, 'r', encoding='utf-8') as f:
loaded_data = yaml.safe_load(f)
except UnicodeDecodeError:
# Fallback encoding for legacy files
with open(data_source, 'r', encoding='gbk') as f:
loaded_data = yaml.safe_load(f)
else:
loaded_data = data_source
# Attach the dataset directly to the function object
func.__dict__['TEST_DATASET'] = loaded_data
return func
return decorator
# Example usage structure for Excel-based injection would follow similar logic
# but parse rows into lists of dicts before calling this decorator.
Estrutura Enxuta da Suíte de Testes Com a lógica de leitura de dados encapsulada em classes auxiliares (como Action ou DoExcel), a classe de teste principal torna-se minimalista. Ela delega a preparação, execução e validação para métodos utilitários, focando apenas no fluxo de controle.
import unittest
from config import Config
from utils.excel_handler import DoExcel
from utils.action_handler import Action
# Load initial configuration and test cases from Excel
excel_path = Config.TEST_CASE_PATH
excel_obj = DoExcel(excel_path)
test_cases, db_configs, init_data, api_host = excel_obj.extract_suite_info()
@inject_test_data(test_cases)
class ApiAutomationSuite(unittest.TestCase):
maxDiff = None
# Shared action instance for all tests in this class
executor = Action(init_data, db_configs)
@classmethod
def setUpClass(cls):
# Dynamically load helper modules or extensions if needed
cls.executor.load_extensions(Config.EXTENSIONS_DIR)
def setUp(self):
pass
def test_endpoint_validation(self, case_item):
# Extract metadata from the current data row
sheet_name, case_id, skip_flag, delay, name, desc, method, expected_result = self.executor.parse_case_metadata(case_item)
# Check skip conditions defined in Excel
if self.executor.should_skip(skip_flag):
self.skipTest(f"Case {case_id} marked as skipped in configuration.")
# Extract request parameters and dependencies
regex_params, keys, deps, json_path_dict, request_payload = self.executor.get_request_details(case_item)
# Apply artificial delay if specified
self.executor.apply_delay(delay)
# Execute pre-test SQL if required
self.executor.run_sql_setup(case_item)
# Skip API call if only SQL validation is intended
if self.executor.is_sql_only(method):
self.skipTest("SQL-only test case detected.")
# Perform HTTP Request
response = self.executor.send_http_request(api_host, method, request_payload)
# Analyze response and extract values for subsequent steps
self.executor.process_response(sheet_name, case_id, name, desc, regex_params, keys, deps, json_path_dict)
# Validate against expected results
self.executor.validate_results(excel_obj, sheet_name, case_id, name, desc, expected_result)
@classmethod
def tearDownClass(cls):
excel_obj.close_file()
Abstração da Leitura de Planilhas A complexidade de mapear células do Excel para objetos Python é isolada em uma classe dedicada. Isso garante que mudanças na estrutura da planilha não quebrem a lógica de teste, desde que os nomes das colunas sejam preservados. A abordagem abaixo demonstra a conversão de linhas em dicionários usando um gerador (yield) para eficiência de memória.
class ExcelParser:
def __init__(self, workbook):
self.wb = workbook
def get_sheet_names(self):
# Assuming sheets are defined in a specific cell or config
return ["Sheet1", "Sheet2"]
def iterate_rows_as_dicts(self, sheet_name):
sheet = self.wb[sheet_name]
max_row = sheet.max_row
max_col = sheet.max_column
headers = []
# Read header row (assuming row 1)
for col_idx in range(1, max_col + 1):
headers.append(sheet.cell(row=1, column=col_idx).value)
# Iterate through data rows starting from row 2
for row_idx in range(2, max_row + 1):
row_data = {}
for col_idx, header in enumerate(headers, start=1):
cell_value = sheet.cell(row=row_idx, column=col_idx).value
row_data[header] = cell_value
# Add sheet context to each record
row_data['_sheet_name'] = sheet_name
yield row_data
Geração de Relatórios HTML com Jinja2 Em vez de depender de bibliotecas de terceiros pesadas como Allure ou HTMLTestRunner nativo, a integração direta com Jinja2 oferece controle total sobre a estética e a estrutura do relatório. O resultado final substitui logs textuais brutos por dashboards visuais que detalham entradas, saídas esperadas e status reais. Essa customização permite incluir gráficos, cores condicionais para falhas/sucessos e links diretos para logs específicos, melhorando significativamente a legibilidade para stakeholders não técnicos.
Distribuição Automática de Resultados A utilidade do framework aumenta quando os relatórios são entregues proativamente. Após a conclusão da suíte de testes, o caminho do arquivo HTML gerado pode ser passado para módulos de notificação. Integrantes comuns incluem:
- SMTP: Para envio de e-mails corporativos com anexos.
- Webhooks (DingTalk/WeCom/Slack): Para mensagens instantâneas em canais de equipe, muitas vezes suportando markdown ou cards interativos. Essa automação elimina a necessidade de configuração manual de servidores CI/CD para visualização básica de testes, reduzindo a sobrecarga operacional associada à manutenção de plug-ins complexos.