teste / tests /README.md
torxyton's picture
feat: Implementa estrutura completa de testes com pytest
b9c68d4

A newer version of the Gradio SDK is available: 6.7.0

Upgrade

Test Suite Documentation

Este diretório contém a suíte completa de testes para o projeto Hugging Face Spaces. O sistema de testes foi projetado seguindo as melhores práticas de desenvolvimento Python, utilizando pytest como framework principal.

📁 Estrutura de Testes

tests/
├── README.md                    # Esta documentação
├── conftest.py                  # Configurações globais e fixtures
├── test_patterns.py             # Exemplos de padrões de teste
├── data/                        # Dados de teste
│   └── sample_market_data.json  # Dados de mercado para testes
├── unit/                        # Testes unitários
│   ├── conftest.py              # Fixtures específicas para testes unitários
│   ├── test_advanced_market_processing.py
│   └── test_voting_system.py
└── integration/                 # Testes de integração
    ├── conftest.py              # Fixtures específicas para testes de integração
    └── test_market_analysis_integration.py

🚀 Como Executar os Testes

Usando o Makefile (Recomendado)

# Executar todos os testes
make test

# Executar apenas testes unitários
make test-unit

# Executar apenas testes de integração
make test-integration

# Executar testes com relatório de cobertura
make test-coverage

# Executar apenas testes rápidos (excluir testes lentos)
make test-fast

# Executar testes em paralelo
make test-parallel

# Executar testes em modo debug
make test-debug

Usando pytest diretamente

# Executar todos os testes
pytest -v

# Executar testes específicos por marcador
pytest -m unit                    # Apenas testes unitários
pytest -m integration             # Apenas testes de integração
pytest -m "not slow"              # Excluir testes lentos
pytest -m performance             # Apenas testes de performance

# Executar testes com cobertura
pytest --cov=src --cov-report=html

# Executar testes em paralelo
pytest -n auto

# Executar testes específicos
pytest tests/unit/test_voting_system.py::test_majority_voting

🏷️ Marcadores de Teste

O sistema utiliza marcadores para categorizar e filtrar testes:

  • unit: Testes unitários
  • integration: Testes de integração
  • slow: Testes que demoram mais para executar
  • api: Testes de API
  • ui: Testes de interface do usuário
  • smoke: Testes de fumaça (básicos)
  • regression: Testes de regressão
  • performance: Testes de performance
  • security: Testes de segurança

Exemplo de uso de marcadores:

import pytest

@pytest.mark.unit
def test_basic_functionality():
    assert True

@pytest.mark.slow
@pytest.mark.performance
def test_large_dataset_processing():
    # Teste que demora mais tempo
    pass

@pytest.mark.integration
@pytest.mark.api
def test_api_integration():
    # Teste de integração com API
    pass

🔧 Configuração

pytest.ini

O arquivo pytest.ini na raiz do projeto contém as configurações principais:

  • Descoberta automática de testes
  • Configuração de relatórios de cobertura
  • Definição de marcadores
  • Configuração de timeout
  • Filtros de warnings

conftest.py

Cada nível possui seu próprio conftest.py:

  • Global (tests/conftest.py): Fixtures compartilhadas por todos os testes
  • Unit (tests/unit/conftest.py): Fixtures específicas para testes unitários
  • Integration (tests/integration/conftest.py): Fixtures para testes de integração

📊 Fixtures Disponíveis

Fixtures Globais

  • project_root: Caminho para a raiz do projeto
  • test_data: Dados de teste carregados
  • temp_dir: Diretório temporário para testes
  • mock_db: Mock do banco de dados
  • sample_market_data: Dados de mercado de exemplo
  • mock_gradio_interface: Mock da interface Gradio
  • mock_transformers: Mock da biblioteca transformers
  • test_env_vars: Variáveis de ambiente para testes
  • caplog_setup: Configuração de captura de logs

Fixtures de Testes Unitários

  • mock_logger: Mock do sistema de logging
  • mock_config: Mock da configuração
  • mock_market_processor: Mock do processador de mercado
  • mock_voting_strategy: Mock da estratégia de votação
  • mock_sentiment_analyzer: Mock do analisador de sentimento
  • mock_fibonacci_analyzer: Mock do analisador de Fibonacci
  • sample_price_data: Dados de preço para testes
  • mock_database_logger: Mock do logger de banco de dados

Fixtures de Testes de Integração

  • temp_db: Banco de dados SQLite temporário
  • db_connection: Conexão com banco de dados de teste
  • populated_db: Banco de dados populado com dados de teste
  • mock_yfinance: Mock da API yfinance
  • mock_requests: Mock da biblioteca requests
  • integration_config: Configuração para testes de integração
  • temp_cache_dir: Diretório de cache temporário

📈 Relatórios de Cobertura

Os relatórios de cobertura são gerados em múltiplos formatos:

  • HTML: htmlcov/index.html - Relatório visual detalhado
  • XML: coverage.xml - Para integração com CI/CD
  • Terminal: Exibido diretamente no terminal

Visualizar relatório HTML:

# Gerar e abrir relatório
make test-coverage
open htmlcov/index.html  # macOS
start htmlcov/index.html # Windows

🔍 Padrões de Teste

O arquivo test_patterns.py demonstra padrões e melhores práticas:

1. Testes Unitários Básicos

def test_basic_functionality():
    """Teste básico de funcionalidade."""
    result = some_function()
    assert result is not None
    assert isinstance(result, expected_type)

2. Uso de Fixtures

def test_with_fixture(sample_data):
    """Teste usando fixture."""
    processor = DataProcessor()
    result = processor.process(sample_data)
    assert result.is_valid

3. Mocking

@patch('module.external_service')
def test_with_mock(mock_service):
    """Teste com mock de serviço externo."""
    mock_service.return_value = expected_response
    result = function_that_uses_service()
    assert result == expected_result
    mock_service.assert_called_once()

4. Testes Parametrizados

@pytest.mark.parametrize("input_value,expected", [
    (1, 2),
    (2, 4),
    (3, 6),
])
def test_multiplication(input_value, expected):
    """Teste parametrizado."""
    assert multiply_by_two(input_value) == expected

5. Tratamento de Exceções

def test_exception_handling():
    """Teste de tratamento de exceções."""
    with pytest.raises(ValueError, match="Invalid input"):
        function_that_should_raise(invalid_input)

6. Testes Assíncronos

@pytest.mark.asyncio
async def test_async_function():
    """Teste de função assíncrona."""
    result = await async_function()
    assert result is not None

🚀 Performance e Benchmarking

Testes de Performance

@pytest.mark.performance
def test_performance_benchmark(benchmark):
    """Teste de benchmark de performance."""
    result = benchmark(expensive_function, large_dataset)
    assert result is not None

Monitoramento de Memória

@pytest.mark.performance
def test_memory_usage():
    """Teste de uso de memória."""
    import psutil
    import os
    
    process = psutil.Process(os.getpid())
    initial_memory = process.memory_info().rss
    
    # Executar operação que consome memória
    result = memory_intensive_operation()
    
    final_memory = process.memory_info().rss
    memory_increase = final_memory - initial_memory
    
    # Verificar se o aumento de memória está dentro do esperado
    assert memory_increase < 100 * 1024 * 1024  # 100MB

🔒 Testes de Segurança

@pytest.mark.security
def test_input_validation():
    """Teste de validação de entrada."""
    malicious_inputs = [
        "<script>alert('xss')</script>",
        "'; DROP TABLE users; --",
        "../../../etc/passwd",
    ]
    
    for malicious_input in malicious_inputs:
        with pytest.raises((ValueError, SecurityError)):
            process_user_input(malicious_input)

📝 Melhores Práticas

1. Nomenclatura

  • Arquivos de teste: test_*.py ou *_test.py
  • Funções de teste: test_*
  • Classes de teste: Test*
  • Fixtures: nomes descritivos sem prefixo test_

2. Organização

  • Um arquivo de teste por módulo
  • Agrupar testes relacionados em classes
  • Usar fixtures para setup/teardown
  • Manter testes independentes

3. Assertions

# Bom: específico e claro
assert result.status == "success"
assert len(result.items) == 3
assert result.total > 0

# Evitar: muito genérico
assert result

4. Documentação

def test_complex_scenario():
    """Teste cenário complexo de processamento de dados.
    
    Este teste verifica se o sistema consegue processar
    corretamente um dataset com múltiplas anomalias.
    """
    # Given: dados com anomalias
    data = create_anomalous_dataset()
    
    # When: processamento é executado
    result = processor.process(data)
    
    # Then: anomalias são detectadas e tratadas
    assert result.anomalies_detected > 0
    assert result.status == "processed_with_warnings"

🔧 Troubleshooting

Problemas Comuns

  1. Testes lentos: Use marcador @pytest.mark.slow e execute com make test-fast
  2. Falhas intermitentes: Verifique dependências externas e use mocks
  3. Problemas de importação: Verifique sys.path no conftest.py
  4. Fixtures não encontradas: Verifique se estão no conftest.py correto

Debug de Testes

# Executar com output detalhado
pytest -v -s

# Executar com debugger
pytest --pdb

# Executar teste específico com debug
pytest tests/unit/test_module.py::test_function -v -s --pdb

Logs durante Testes

import logging

def test_with_logging(caplog):
    """Teste com captura de logs."""
    with caplog.at_level(logging.INFO):
        function_that_logs()
    
    assert "Expected log message" in caplog.text
    assert caplog.records[0].levelname == "INFO"

📚 Recursos Adicionais

🤝 Contribuindo

Ao adicionar novos testes:

  1. Siga os padrões estabelecidos
  2. Adicione marcadores apropriados
  3. Documente testes complexos
  4. Mantenha cobertura > 80%
  5. Execute make pre-commit antes de commitar

Nota: Esta documentação é mantida junto com o código. Mantenha-a atualizada conforme o sistema evolui.