Testowanie - emielregis2/SmartFlowAI GitHub Wiki

Testowanie - SmartFlowAI

Poznaj kompletny system testowania SmartFlowAI - od testów jednostkowych po testy end-to-end! 🧪

🎯 Przegląd testowania

SmartFlowAI używa wielopoziomowej strategii testowania zapewniającej wysoką jakość kodu:

┌─────────────────┐
│   E2E Tests     │ ← Testy całej aplikacji
├─────────────────┤
│ Integration     │ ← Testy integracji z API
├─────────────────┤
│   Unit Tests    │ ← Testy jednostkowe funkcji
└─────────────────┘

📊 Statystyki testów

Typ testu Pliki Testy Pokrycie Czas wykonania
Jednostkowe 3 25+ 80%+ <30s
Integracyjne 2 15+ 70%+ 1-2min
End-to-End 1 10+ 90%+ 3-5min
Produkcyjne 1 20+ 95%+ 2-3min

🧪 Typy testów

1. Testy jednostkowe (Unit Tests)

test_app.py - Podstawowe testy

def test_validate_process_input():
    """Test walidacji danych wejściowych procesu"""
    # Test poprawnych danych
    valid, msg = validate_process_input("Test proces", "Szczegółowy opis procesu")
    assert valid == True
    
    # Test niepoprawnych danych
    valid, msg = validate_process_input("", "Opis")
    assert valid == False
    assert "minimum 3 znaki" in msg

test_simple_working.py - Testy funkcjonalności

def test_password_hashing():
    """Test hashowania haseł"""
    password = "test123456"
    hashed = hash_password(password)
    assert verify_password(password, hashed) == True
    assert verify_password("wrong", hashed) == False

def test_openai_prompt_generation():
    """Test generowania promptów dla AI"""
    prompt = create_analysis_prompt(
        title="Test proces",
        description="Opis procesu",
        depth="Podstawowa",
        company_size="1-10 osób",
        industry="IT",
        budget="500-1000 zł/mies"
    )
    assert "Test proces" in prompt
    assert "Podstawowa" in prompt

2. Testy integracyjne (Integration Tests)

test_comprehensive.py - Testy z bazą danych

def test_supabase_connection():
    """Test połączenia z Supabase"""
    supabase = init_supabase()
    assert supabase is not None
    
    # Test zapytania do bazy
    result = supabase.table('users').select('*').limit(1).execute()
    assert result.data is not None

def test_user_authentication():
    """Test autentykacji użytkowników"""
    # Test logowania z prawidłowymi danymi
    user = authenticate_user("[email protected]", "test123456")
    assert user is not None
    assert user['email'] == "[email protected]"
    
    # Test logowania z nieprawidłowymi danymi
    user = authenticate_user("[email protected]", "wrong_password")
    assert user is None

test_enhanced_analysis.py - Testy analizy AI

def test_openai_api_connection():
    """Test połączenia z OpenAI API"""
    client = init_openai()
    assert client is not None
    
    # Test prostego zapytania
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Test"}],
        max_tokens=10
    )
    assert response.choices[0].message.content is not None

def test_process_analysis():
    """Test analizy procesu przez AI"""
    analysis = analyze_process_with_ai(
        title="Test proces",
        description="Szczegółowy opis procesu testowego",
        depth="Podstawowa",
        company_size="1-10 osób",
        industry="IT",
        budget="500-1000 zł/mies"
    )
    assert analysis is not None
    assert len(analysis) > 100  # Analiza powinna być szczegółowa

3. Testy end-to-end (E2E Tests)

test_e2e.py - Pełny przepływ aplikacji

def test_complete_user_workflow():
    """Test kompletnego przepływu użytkownika"""
    # 1. Logowanie
    user = authenticate_user("[email protected]", "test123456")
    assert user is not None
    
    # 2. Dodanie procesu
    process_data = {
        'title': 'E2E Test Process',
        'description': 'Proces testowy dla testów end-to-end',
        'user_id': user['id']
    }
    saved_process = save_process(process_data)
    assert saved_process is not None
    
    # 3. Analiza AI
    analysis = analyze_process_with_ai(
        title=process_data['title'],
        description=process_data['description'],
        depth="Podstawowa",
        company_size="1-10 osób",
        industry="IT",
        budget="500-1000 zł/mies"
    )
    assert analysis is not None
    
    # 4. Aktualizacja procesu z analizą
    updated_process = update_process(saved_process['id'], {'ai_analysis': analysis})
    assert updated_process is not None
    
    # 5. Pobranie procesów użytkownika
    user_processes = get_processes(user['id'])
    assert len(user_processes) > 0
    
    # 6. Usunięcie procesu (cleanup)
    delete_result = delete_process(saved_process['id'])
    assert delete_result == True

4. Testy produkcyjne (Production Tests)

test_production_ready.py - Testy gotowości produkcyjnej

def test_environment_variables():
    """Test czy wszystkie wymagane zmienne środowiskowe są ustawione"""
    required_vars = [
        'SUPABASE_URL',
        'SUPABASE_ANON_KEY', 
        'SUPABASE_SERVICE_ROLE_KEY',
        'OPENAI_API_KEY'
    ]
    
    for var in required_vars:
        value = os.getenv(var)
        assert value is not None, f"Zmienna {var} nie jest ustawiona"
        assert len(value) > 10, f"Zmienna {var} wydaje się niepoprawna"

def test_database_schema():
    """Test czy schemat bazy danych jest poprawny"""
    supabase = init_supabase()
    
    # Test tabeli users
    users = supabase.table('users').select('*').limit(1).execute()
    assert users.data is not None
    
    # Test tabeli business_processes
    processes = supabase.table('business_processes').select('*').limit(1).execute()
    assert processes.data is not None

def test_api_rate_limits():
    """Test limitów API"""
    # Test czy OpenAI API nie przekracza limitów
    start_time = time.time()
    
    for i in range(3):
        response = openai.ChatCompletion.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": f"Test {i}"}],
            max_tokens=10
        )
        assert response.choices[0].message.content is not None
    
    elapsed_time = time.time() - start_time
    assert elapsed_time < 30, "API calls took too long"

🚀 Uruchamianie testów

Przygotowanie środowiska

# Aktywuj środowisko wirtualne
.venv\Scripts\activate  # Windows
source .venv/bin/activate  # Linux/macOS

# Zainstaluj zależności testowe
pip install -r requirements-dev.txt

# Ustaw zmienne środowiskowe testowe
export SUPABASE_URL="https://test-projekt.supabase.co"
export OPENAI_API_KEY="sk-test-key"

Uruchamianie poszczególnych testów

Testy podstawowe

# Wszystkie testy podstawowe
pytest test_app.py -v

# Konkretny test
pytest test_app.py::test_validate_process_input -v

# Z pokryciem kodu
pytest test_app.py --cov=streamlit_app --cov-report=html

Testy integracyjne

# Testy z bazą danych (wymagają konfiguracji Supabase)
pytest test_comprehensive.py -v

# Testy analizy AI (wymagają klucza OpenAI)
pytest test_enhanced_analysis.py -v

Testy end-to-end

# Pełny przepływ aplikacji
pytest test_e2e.py -v

# Z szczegółowymi logami
pytest test_e2e.py -v -s --log-cli-level=INFO

Testy produkcyjne

# Sprawdzenie gotowości do produkcji
pytest test_production_ready.py -v

# Tylko krytyczne testy
pytest test_production_ready.py -k "critical" -v

Uruchamianie wszystkich testów

# Wszystkie testy
pytest -v

# Z raportem pokrycia
pytest --cov=streamlit_app --cov-report=html --cov-report=term

# Tylko szybkie testy (bez AI)
pytest -m "not slow" -v

# Równoległe wykonanie
pytest -n 4 -v  # 4 procesy równolegle

📋 Konfiguracja testów

pytest.ini - Konfiguracja pytest

[tool:pytest]
testpaths = .
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = 
    -v
    --tb=short
    --strict-markers
    --disable-warnings
    --color=yes
markers =
    slow: marks tests as slow (deselect with '-m "not slow"')
    integration: marks tests as integration tests
    e2e: marks tests as end-to-end tests
    production: marks tests as production readiness tests

Zmienne środowiskowe testowe

# .env.test
SUPABASE_URL=https://test-projekt.supabase.co
SUPABASE_ANON_KEY=test_anon_key
SUPABASE_SERVICE_ROLE_KEY=test_service_key
OPENAI_API_KEY=sk-test-key
DEMO_MODE=true

🔧 Narzędzia testowe

Pytest - Framework testowy

# Instalacja
pip install pytest pytest-cov pytest-xdist

# Podstawowe użycie
pytest                    # Wszystkie testy
pytest test_app.py       # Konkretny plik
pytest -k "test_auth"    # Testy zawierające "auth"
pytest -x                # Stop po pierwszym błędzie

Coverage - Pokrycie kodu

# Raport pokrycia
pytest --cov=streamlit_app

# HTML raport
pytest --cov=streamlit_app --cov-report=html
# Otwórz htmlcov/index.html w przeglądarce

# Raport XML (dla CI/CD)
pytest --cov=streamlit_app --cov-report=xml

Fixtures - Dane testowe

# conftest.py
import pytest

@pytest.fixture
def test_user():
    """Fixture z danymi testowego użytkownika"""
    return {
        'email': '[email protected]',
        'password': 'test123456',
        'full_name': 'Test User'
    }

@pytest.fixture
def test_process():
    """Fixture z danymi testowego procesu"""
    return {
        'title': 'Test Process',
        'description': 'Szczegółowy opis testowego procesu biznesowego',
        'analysis_depth': 'Podstawowa',
        'company_size': '1-10 osób',
        'industry': 'IT',
        'budget_range': '500-1000 zł/mies'
    }

🐛 Debugging testów

Uruchamianie z debuggerem

# Z pdb debuggerem
pytest --pdb test_app.py

# Z ipdb (lepszy debugger)
pip install ipdb
pytest --pdb --pdbcls=IPython.terminal.debugger:Pdb test_app.py

# Tylko przy błędach
pytest --pdb-trace test_app.py

Logi w testach

import logging

def test_with_logging():
    """Test z logowaniem"""
    logger = logging.getLogger(__name__)
    logger.info("Starting test")
    
    # Twój kod testowy
    result = some_function()
    logger.info(f"Result: {result}")
    
    assert result is not None

Uruchamianie z logami

# Pokaż logi w testach
pytest -s --log-cli-level=INFO test_app.py

# Zapisz logi do pliku
pytest --log-file=tests.log test_app.py

📊 Raporty testów

HTML Report

# Generuj raport HTML
pytest --html=report.html --self-contained-html

# Z pokryciem kodu
pytest --cov=streamlit_app --cov-report=html --html=report.html

JUnit XML (dla CI/CD)

# Raport XML dla Jenkins/GitHub Actions
pytest --junitxml=test-results.xml

# Z pokryciem
pytest --cov=streamlit_app --cov-report=xml --junitxml=test-results.xml

Allure Reports (zaawansowane)

# Instalacja
pip install allure-pytest

# Generowanie danych
pytest --alluredir=allure-results

# Serwowanie raportu
allure serve allure-results

✅ Best Practices testowania

1. Struktura testów (AAA Pattern)

def test_user_authentication():
    # Arrange - Przygotowanie danych
    email = "[email protected]"
    password = "test123456"
    
    # Act - Wykonanie akcji
    user = authenticate_user(email, password)
    
    # Assert - Sprawdzenie wyników
    assert user is not None
    assert user['email'] == email

2. Nazewnictwo testów

# ✅ Dobre nazwy
def test_should_return_user_when_credentials_are_valid():
def test_should_raise_exception_when_email_is_empty():
def test_should_generate_pdf_with_all_processes():

# ❌ Złe nazwy
def test_user():
def test_login():
def test_pdf():

3. Izolacja testów

@pytest.fixture(autouse=True)
def cleanup_database():
    """Automatyczne czyszczenie bazy po każdym teście"""
    yield  # Test się wykonuje
    
    # Cleanup po teście
    supabase.table('business_processes').delete().neq('id', '').execute()

4. Mockowanie zewnętrznych serwisów

from unittest.mock import patch, MagicMock

@patch('openai.ChatCompletion.create')
def test_ai_analysis_with_mock(mock_openai):
    """Test analizy AI z mockiem OpenAI"""
    # Arrange
    mock_response = MagicMock()
    mock_response.choices[0].message.content = "Mocked AI response"
    mock_openai.return_value = mock_response
    
    # Act
    result = analyze_process_with_ai("Test", "Description", "Basic", "1-10", "IT", "500-1000")
    
    # Assert
    assert result == "Mocked AI response"
    mock_openai.assert_called_once()

🚨 Troubleshooting testów

Częste problemy

Problem: "ModuleNotFoundError"

# Rozwiązanie: Dodaj ścieżkę do PYTHONPATH
export PYTHONPATH="${PYTHONPATH}:$(pwd)"
pytest test_app.py

Problem: "Database connection failed"

# Sprawdź zmienne środowiskowe
echo $SUPABASE_URL
echo $SUPABASE_ANON_KEY

# Uruchom z debug mode
pytest -s --log-cli-level=DEBUG test_comprehensive.py

Problem: "OpenAI API rate limit"

# Użyj mocków dla testów jednostkowych
@patch('openai.ChatCompletion.create')
def test_with_mock(mock_openai):
    # Test bez prawdziwego API call

Problem: "Tests are too slow"

# Uruchom tylko szybkie testy
pytest -m "not slow"

# Równoległe wykonanie
pip install pytest-xdist
pytest -n 4  # 4 procesy

📈 Metryki jakości

Cele pokrycia kodu

  • Funkcje główne: 95%+
  • Logika biznesowa: 90%+
  • Obsługa błędów: 80%+
  • UI komponenty: 70%+

Kryteria akceptacji

  • ✅ Wszystkie testy przechodzą
  • ✅ Pokrycie kodu > 80%
  • ✅ Brak błędów krytycznych
  • ✅ Czas wykonania < 5 minut
  • ✅ Zero podatności bezpieczeństwa

Następny krok: CI/CD - poznaj automatyzację testów i deploymentu w GitHub Actions.