Testes e Qualidade¶
📋 Visão Geral¶
O Motor de Pricing utiliza pytest como framework de testes. A suíte de testes está organizada no diretório tests/ e conta com um script auxiliar (run_tests.py) para facilitar a execução.
🧪 Executando Testes¶
Comandos Básicos¶
# Todos os testes
pytest
# Testes de um módulo específico
pytest tests/test_price_list_page.py
# Teste específico
pytest tests/test_price_list_page.py::TestCalculations::test_update_calculated_columns_preco_final_changed
# Classe de testes
pytest tests/test_price_list_page.py::TestCalculations
Com Cobertura¶
# Cobertura básica
pytest --cov=pages
# Cobertura com relatório HTML
pytest --cov=pages --cov-report=html
# Cobertura de módulo específico
pytest --cov=pages.price_list.price_list_page --cov-report=term-missing
# Falhar se cobertura < 90%
pytest --cov=pages --cov-fail-under=90
Script Auxiliar¶
# Todos os testes
python run_tests.py
# Com cobertura
python run_tests.py --coverage
# Módulo específico
python run_tests.py --module price_list
# Modo verboso
python run_tests.py --verbose
# Apenas testes que falharam
python run_tests.py --failed
# Em paralelo (mais rápido)
python run_tests.py --parallel
📂 Estrutura de Testes¶
tests/
├── conftest.py # Fixtures compartilhadas
├── test_price_list_page.py # Testes da lista de preços
├── test_calculations.py # Testes de cálculos
└── ... # Outros arquivos de teste
✅ Padrão de Testes¶
Todos os testes seguem o padrão Arrange-Act-Assert com docstrings descritivas:
class TestMyFeature:
"""Testes para minha funcionalidade"""
def test_algo_funciona(self, sample_data):
"""
Testa que algo funciona corretamente
Given: Dados de entrada válidos
When: Função é chamada
Then: Resultado esperado é retornado
"""
# Arrange (preparar)
input_data = sample_data
expected = "resultado esperado"
# Act (executar)
result = my_function(input_data)
# Assert (verificar)
assert result == expected
🔧 Fixtures Disponíveis¶
@pytest.fixture
def sample_price_data():
"""Dados de exemplo de preços"""
return [{"preco_final": 100.0, "a_custo_projetado": 60.0, "mb_final": 0.4}]
@pytest.fixture
def mock_user_data():
"""Usuário autenticado"""
return {"user_id": "test-user", "email": "test@lavoro.com"}
🎭 Mocking¶
Mock de Dependências Externas¶
As chamadas à API (BigQuery, Dataform) são mockadas para testes unitários:
@patch('pages.price_list.price_list_page.fetch_data')
def test_with_mock(mock_fetch):
mock_fetch.return_value = [{"id": 1}]
result = get_table_data("request-id")
mock_fetch.assert_called_once()
assert result == [{"id": 1}]
Mock de Callback Context¶
@patch('pages.price_list.price_list_page.ctx')
def test_callback(mock_ctx):
mock_ctx.triggered = [{"prop_id": "button.n_clicks"}]
result = my_callback(1, 2, 3)
assert result is not None
🏷️ Markers¶
@pytest.mark.slow # Teste lento
@pytest.mark.unit # Teste unitário
# Executar apenas marcados
pytest -m unit
pytest -m "not slow"
🚀 CI/CD¶
Configuração para GitHub Actions:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- run: pip install -r requirements-test.txt
- run: pytest --cov=pages --cov-report=xml
- uses: codecov/codecov-action@v2
📋 Checklist para Novos Testes¶
- Nome descritivo (
test_what_when_then) - Docstring explicando contexto (Given/When/Then)
- Arrange-Act-Assert organizado
- Usa fixtures quando apropriado
- Mocks de dependências externas (BigQuery, Dataform)
- Testa casos normais e edge cases
- Assertions claras e específicas
- Cobertura do código testado > 90%
Última atualização: Março 2026 | Versão 1.0