Pular para conteúdo

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