- rules/testing.md: TDD workflow for Go and Python - rules/documentation.md: Auto-documentation guidelines - plans/embedding-service-separation.md: Migration plan - settings.json: Post-edit hooks for docs/tests validation - .gitignore: Exclude settings.local.json (contains API keys) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
203 lines
4.9 KiB
Markdown
203 lines
4.9 KiB
Markdown
# Test-Regeln
|
|
|
|
## Automatische Test-Erweiterung
|
|
|
|
**WICHTIG:** Bei JEDER Code-Änderung müssen entsprechende Tests erstellt oder aktualisiert werden!
|
|
|
|
## Wann Tests schreiben?
|
|
|
|
### IMMER wenn du:
|
|
1. **Neue Funktionen** erstellst → Unit Test
|
|
2. **Neue API-Endpoints** hinzufügst → Handler Test
|
|
3. **Bugs fixst** → Regression Test (der Bug sollte nie wieder auftreten)
|
|
4. **Bestehenden Code änderst** → Bestehende Tests anpassen
|
|
|
|
## Test-Struktur
|
|
|
|
### Go Tests (Consent Service)
|
|
|
|
**Speicherort:** Im gleichen Verzeichnis wie der Code
|
|
|
|
```
|
|
internal/
|
|
├── services/
|
|
│ ├── auth_service.go
|
|
│ └── auth_service_test.go ← Test hier
|
|
├── handlers/
|
|
│ ├── handlers.go
|
|
│ └── handlers_test.go ← Test hier
|
|
└── middleware/
|
|
├── auth.go
|
|
└── middleware_test.go ← Test hier
|
|
```
|
|
|
|
**Test-Namenskonvention:**
|
|
```go
|
|
func TestFunctionName_Scenario_ExpectedResult(t *testing.T)
|
|
|
|
// Beispiele:
|
|
func TestHashPassword_ValidPassword_ReturnsHash(t *testing.T)
|
|
func TestLogin_InvalidCredentials_Returns401(t *testing.T)
|
|
func TestCreateDocument_MissingTitle_ReturnsError(t *testing.T)
|
|
```
|
|
|
|
**Test-Template:**
|
|
```go
|
|
func TestFunctionName(t *testing.T) {
|
|
// Arrange
|
|
service := &MyService{}
|
|
input := "test-input"
|
|
|
|
// Act
|
|
result, err := service.DoSomething(input)
|
|
|
|
// Assert
|
|
if err != nil {
|
|
t.Fatalf("Expected no error, got %v", err)
|
|
}
|
|
if result != expected {
|
|
t.Errorf("Expected %v, got %v", expected, result)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Table-Driven Tests bevorzugen:**
|
|
```go
|
|
func TestValidateEmail(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
email string
|
|
expected bool
|
|
}{
|
|
{"valid email", "test@example.com", true},
|
|
{"missing @", "testexample.com", false},
|
|
{"empty", "", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := ValidateEmail(tt.email)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected %v, got %v", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
### Python Tests (Backend)
|
|
|
|
**Speicherort:** `/backend/tests/`
|
|
|
|
```
|
|
backend/
|
|
├── consent_client.py
|
|
├── gdpr_api.py
|
|
└── tests/
|
|
├── __init__.py
|
|
├── test_consent_client.py ← Tests für consent_client.py
|
|
└── test_gdpr_api.py ← Tests für gdpr_api.py
|
|
```
|
|
|
|
**Test-Namenskonvention:**
|
|
```python
|
|
class TestClassName:
|
|
def test_method_scenario_expected_result(self):
|
|
pass
|
|
|
|
# Beispiele:
|
|
class TestConsentClient:
|
|
def test_check_consent_valid_token_returns_status(self):
|
|
pass
|
|
|
|
def test_check_consent_expired_token_raises_error(self):
|
|
pass
|
|
```
|
|
|
|
**Test-Template:**
|
|
```python
|
|
import pytest
|
|
from unittest.mock import AsyncMock, patch, MagicMock
|
|
|
|
class TestMyFeature:
|
|
def test_sync_function(self):
|
|
# Arrange
|
|
input_data = "test"
|
|
|
|
# Act
|
|
result = my_function(input_data)
|
|
|
|
# Assert
|
|
assert result == expected
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_function(self):
|
|
# Arrange
|
|
client = MyClient()
|
|
|
|
# Act
|
|
with patch("httpx.AsyncClient") as mock:
|
|
mock_instance = AsyncMock()
|
|
mock.return_value = mock_instance
|
|
result = await client.fetch_data()
|
|
|
|
# Assert
|
|
assert result is not None
|
|
```
|
|
|
|
## Test-Kategorien
|
|
|
|
### 1. Unit Tests (Höchste Priorität)
|
|
- Testen einzelne Funktionen/Methoden
|
|
- Keine externen Abhängigkeiten (Mocks verwenden)
|
|
- Schnell ausführbar
|
|
|
|
### 2. Integration Tests
|
|
- Testen Zusammenspiel mehrerer Komponenten
|
|
- Können echte DB verwenden (Test-DB)
|
|
|
|
### 3. Security Tests
|
|
- Auth/JWT Validierung
|
|
- Passwort-Hashing
|
|
- Berechtigungsprüfung
|
|
|
|
## Checkliste vor Abschluss
|
|
|
|
Vor dem Abschluss einer Aufgabe:
|
|
|
|
- [ ] Gibt es Tests für alle neuen Funktionen?
|
|
- [ ] Gibt es Tests für alle Edge Cases?
|
|
- [ ] Gibt es Tests für Fehlerfälle?
|
|
- [ ] Laufen alle bestehenden Tests noch? (`go test ./...` / `pytest`)
|
|
- [ ] Ist die Test-Coverage angemessen?
|
|
|
|
## Tests ausführen
|
|
|
|
```bash
|
|
# Go - Alle Tests
|
|
cd consent-service && go test -v ./...
|
|
|
|
# Go - Mit Coverage
|
|
cd consent-service && go test -cover ./...
|
|
|
|
# Python - Alle Tests
|
|
cd backend && source venv/bin/activate && pytest -v
|
|
|
|
# Python - Mit Coverage
|
|
cd backend && pytest --cov=. --cov-report=html
|
|
```
|
|
|
|
## Beispiel: Vollständiger Test-Workflow
|
|
|
|
Wenn du z.B. eine neue `GetUserStats()` Funktion im Go Service hinzufügst:
|
|
|
|
1. **Funktion schreiben** in `internal/services/stats_service.go`
|
|
2. **Test erstellen** in `internal/services/stats_service_test.go`:
|
|
```go
|
|
func TestGetUserStats_ValidUser_ReturnsStats(t *testing.T) {...}
|
|
func TestGetUserStats_InvalidUser_ReturnsError(t *testing.T) {...}
|
|
func TestGetUserStats_NoConsents_ReturnsEmptyStats(t *testing.T) {...}
|
|
```
|
|
3. **Tests ausführen**: `go test -v ./internal/services/...`
|
|
4. **Dokumentation aktualisieren** (siehe documentation.md)
|