Ce document décrit la stratégie de tests, les outils utilisés, et les fonctionnalités couvertes par les tests automatisés.
- Vue d'ensemble
- Backend (Django/pytest)
- Frontend (Angular/Jest)
- Exécution des tests
- Fonctionnalités testées
- Prochaines étapes
- CI/CD avec GitHub Actions
| Stack | Framework | Tests | Couverture | Type |
|---|---|---|---|---|
| Backend | pytest + pytest-django | 317 | 62% | Unitaires + Intégration |
| Frontend | Jest + jest-preset-angular | 55 | 10% global, 100% auth | Unitaires |
| E2E | Playwright | ~80 | Voir détail ci-dessous | End-to-End |
| Total | - | ~452 | - | - |
Cicada/
├── backend/
│ ├── pytest.ini # Configuration pytest
│ ├── conftest.py # Fixtures globales
│ └── tests/
│ ├── factories/ # Factory Boy factories
│ │ ├── users.py
│ │ ├── plans.py
│ │ └── core.py
│ ├── apps/ # Tests unitaires
│ │ ├── users/
│ │ │ ├── test_models.py
│ │ │ ├── test_permissions.py
│ │ │ └── test_middleware.py
│ │ └── plans/
│ │ ├── test_views.py
│ │ └── test_filters.py
│ └── integration/ # Tests d'intégration API
│ ├── test_api_auth.py
│ ├── test_api_users.py
│ ├── test_api_org_sites.py
│ └── test_api_plans.py
│
├── frontend/
│ ├── jest.config.js # Configuration Jest
│ ├── setup-jest.ts # Setup environnement
│ ├── tsconfig.spec.json # TypeScript pour tests
│ └── src/app/core/
│ ├── services/
│ │ └── auth.service.spec.ts
│ ├── guards/
│ │ └── auth.guard.spec.ts
│ └── interceptors/
│ └── auth.interceptor.spec.ts
│
└── frontend/e2e/ # Tests E2E Playwright
├── playwright.config.ts # Configuration Playwright
├── global-setup.ts # Attente services + seed données
├── fixtures/
│ ├── auth.setup.ts # Login des 6 utilisateurs de test
│ └── auth.fixture.ts # Pages pré-authentifiées par rôle
├── helpers/
│ ├── api.helper.ts # Appels API directs
│ └── wait.helper.ts # Utilitaires d'attente
├── pages/ # Page Objects (11 fichiers)
└── tests/
├── auth/ # Login, logout, register (13 tests)
├── admin/ # Users, sites, validations, etc. (46 tests)
├── access/ # Contrôle d'accès par rôle (13 tests)
└── navigation/ # Navigation et header (4 tests)
| Outil | Version | Usage |
|---|---|---|
| pytest | 7.4+ | Framework de test |
| pytest-django | 4.8+ | Intégration Django |
| pytest-cov | 4.1+ | Rapports de couverture |
| factory-boy | 3.3+ | Génération de données de test |
| Faker | 38+ | Données aléatoires réalistes |
Tests isolés des composants individuels sans dépendances externes.
Modèles (test_models.py)
@pytest.mark.unit
class TestRoleModel:
def test_create_user(self, db):
user = Role.objects.create_user(email='test@example.com', password='test')
assert user.email == 'test@example.com'Permissions (test_permissions.py)
@pytest.mark.unit
class TestIsSuperAdminPermission:
def test_super_admin_has_permission(self):
permission = IsSuperAdmin()
assert permission.has_permission(request, view) is TrueMiddleware (test_middleware.py)
@pytest.mark.unit
class TestPermissionMiddleware:
def test_adds_permission_headers(self):
response = middleware(request)
assert 'X-User-Role' in response.headersTests des endpoints API complets avec base de données.
@pytest.mark.django_db
@pytest.mark.integration
class TestUsersListEndpoint:
def test_list_super_admin_sees_all(self, api_client):
admin = SuperAdminFactory()
api_client.force_authenticate(user=admin)
response = api_client.get('/api/users/users/')
assert response.status_code == 200| Factory | Fichier | Description |
|---|---|---|
UserFactory |
users.py | Utilisateur standard |
SuperAdminFactory |
users.py | Super administrateur |
AdminOrganismeFactory |
users.py | Admin d'organisme |
ReferentFactory |
users.py | Référent de site |
OrganismeFactory |
users.py | Organisation |
SiteFactory |
users.py | Site protégé (avec géométrie) |
PlanGestionFactory |
plans.py | Plan de gestion |
CorSitePgFactory |
plans.py | Association site-plan |
CorPgFichierFactory |
plans.py | Fichier attaché |
NomenclatureTypeFactory |
core.py | Type de nomenclature |
NomenclatureFactory |
core.py | Nomenclature |
# Clients API
api_client # Client non authentifié
authenticated_client # (client, user) - utilisateur standard
super_admin_client # (client, admin) - super admin
admin_og_client # (client, admin_og) - admin organisme
referent_client # (client, referent) - référent
# Factories (accessibles dans les tests)
user_factory
super_admin_factory
organisme_factory
site_factory
plan_factory
nomenclature_factory@pytest.mark.unit # Test unitaire
@pytest.mark.integration # Test d'intégration
@pytest.mark.slow # Test lent (> 1s)| Outil | Version | Usage |
|---|---|---|
| Jest | 29.7+ | Framework de test |
| jest-preset-angular | 14+ | Preset Angular |
| @types/jest | 29.5+ | Types TypeScript |
describe('AuthService', () => {
it('should login successfully', fakeAsync(() => {
service.login({ username: 'test@example.com', password: 'password' }).subscribe();
const req = httpMock.expectOne('/api/auth/login/');
req.flush(mockLoginResponse);
expect(service.isAuthenticated()).toBe(true);
}));
});describe('authGuard', () => {
it('should allow access when authenticated', () => {
mockAuthService.isAuthenticated.mockReturnValue(true);
const result = TestBed.runInInjectionContext(() => authGuard(mockRoute, mockState));
expect(result).toBe(true);
});
});describe('AuthInterceptor', () => {
it('should add Authorization header', () => {
mockAuthService.getAccessToken.mockReturnValue('token');
httpClient.get('/api/users/').subscribe();
const req = httpMock.expectOne('/api/users/');
expect(req.request.headers.get('Authorization')).toBe('Bearer token');
});
});// jest.config.js
module.exports = {
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
collectCoverageFrom: [
'src/app/**/*.ts',
'!src/app/**/*.module.ts',
'!src/app/**/*.routes.ts'
],
moduleNameMapper: {
'@app/(.*)': '<rootDir>/src/app/$1',
'@core/(.*)': '<rootDir>/src/app/core/$1'
}
};# Via Docker (recommandé)
docker compose exec web pytest tests/
# Tous les tests avec couverture
docker compose exec web pytest tests/ --cov=apps --cov-report=html
# Tests unitaires uniquement
docker compose exec web pytest tests/ -m unit
# Tests d'intégration uniquement
docker compose exec web pytest tests/ -m integration
# Un fichier spécifique
docker compose exec web pytest tests/integration/test_api_users.py -v
# Un test spécifique
docker compose exec web pytest tests/integration/test_api_users.py::TestUsersListEndpoint::test_list_super_admin_sees_all -v
# Mode verbose avec détails d'erreur
docker compose exec web pytest tests/ -v --tb=longcd frontend
# Tous les tests
npm test
# Mode watch (développement)
npm run test:watch
# Avec couverture
npm run test:coverage
# Un fichier spécifique
npm test -- src/app/core/services/auth.service.spec.ts| Fonctionnalité | Tests | Couverture |
|---|---|---|
| Modèles | ||
| Création utilisateur | ✅ | 94% |
| Méthodes de rôle (is_super_admin, etc.) | ✅ | 100% |
| Relations organisme/site | ✅ | 90% |
| Permissions DRF | ||
| IsSuperAdmin | ✅ | 100% |
| IsAdminOrganisme | ✅ | 100% |
| IsReferent | ✅ | 100% |
| CanManageOrganisme | ✅ | 100% |
| CanManageSite | ✅ | 100% |
| HasPlanGestionAccess | ✅ | 100% |
| Middleware | ||
| PermissionMiddleware | ✅ | 95% |
| SecurityHeadersMiddleware | ✅ | 100% |
| AuditMiddleware | ✅ | 90% |
| API Users | ||
| CRUD utilisateurs | ✅ | 88% |
| Endpoint /me | ✅ | 100% |
| Changement mot de passe | ✅ | 100% |
| Assignation sites | ✅ | 100% |
| Filtres et recherche | ✅ | 71% |
| Pagination | ✅ | 100% |
| API Organismes | ||
| CRUD organismes | ✅ | 78% |
| Hiérarchie parent/enfant | ✅ | 80% |
| Statistiques | ✅ | 70% |
| API Sites | ||
| CRUD sites | ✅ | 78% |
| GeoJSON import/export | ✅ | 75% |
| Filtres géospatiaux | 57% |
| Fonctionnalité | Tests | Couverture |
|---|---|---|
| Modèles | ||
| PlanGestion CRUD | ✅ | 75% |
| CorSitePg (multi-sites) | ✅ | 80% |
| CorPgFichier (attachements) | ✅ | 70% |
| API Plans | ||
| CRUD plans | ✅ | 59% |
| Assignation sites | ✅ | 100% |
| Endpoint stats | ✅ | 100% |
| Endpoint geojson_list | ✅ | 80% |
| Filtres avancés | 62% | |
| Export GeoJSON | ✅ | 80% |
| Fonctionnalité | Tests | Couverture |
|---|---|---|
| Login JWT | ✅ | 94% |
| Refresh token | ✅ | 100% |
| Logout | 44% (blacklist non activé) | |
| Endpoint /me | ✅ | 100% |
| Impersonation | 44% |
| Fonctionnalité | Tests | Couverture |
|---|---|---|
| AuthService | ||
| Login/Logout | ✅ | 74% |
| Token management | ✅ | 80% |
| Role checking | ✅ | 100% |
| Signals (currentUser, etc.) | ✅ | 90% |
| Impersonation | ✅ | 60% |
| Guards | ||
| authGuard | ✅ | 100% |
| roleGuard | ✅ | 100% |
| adminGuard | ✅ | 100% |
| guestGuard | ✅ | 100% |
| notAdminOgOnlyGuard | ✅ | 100% |
| Interceptor | ||
| Token injection | ✅ | 100% |
| 401 handling | ✅ | 100% |
| Token refresh | ✅ | 100% |
- ✅ Bien couvert (>70%)
⚠️ Partiellement couvert (40-70%)- ❌ Non couvert (<40%)
| Outil | Version | Usage |
|---|---|---|
| @playwright/test | 1.49+ | Framework E2E |
| Chromium | (bundled) | Navigateur de test |
Les tests E2E s'exécutent contre le stack Docker réel (Django + PostgreSQL + Redis + Angular).
# 1. Démarrer les services Docker
docker compose up -d
# 2. S'assurer que les données de test existent
docker compose exec web python manage.py seed_testdatacd frontend
# Tous les tests (headless)
npm run e2e
# Interface visuelle Playwright
npm run e2e:ui
# Tests visibles dans le navigateur
npm run e2e:headed
# Mode debug avec inspector
npm run e2e:debug
# Générer des tests via enregistrement
npm run e2e:codegenLes tests utilisent le mécanisme storageState de Playwright : un projet auth-setup se connecte via l'UI pour 6 utilisateurs de test et sauvegarde les tokens JWT dans des fichiers .auth/*.json. Les tests suivants réutilisent ces fichiers sans se reconnecter.
| Utilisateur | Rôle | Fichier storageState | |
|---|---|---|---|
| Super Admin | admin@test.fr |
super_admin | super-admin.json |
| Admin RNF | admin.rnf@test.fr |
admin_og | admin-rnf.json |
| Admin CEN | admin.cen@test.fr |
admin_og | admin-cen.json |
| Référent | referent.camargue@test.fr |
referent | referent.json |
| User RNF | user.rnf@test.fr |
utilisateur | user-rnf.json |
| User CEN | user.cen@test.fr |
utilisateur | user-cen.json |
Mot de passe commun : Test123!
// Usage dans les tests :
test('admin voit le dashboard', async ({ superAdminPage }) => {
await superAdminPage.goto('/administration/dashboard');
});
test('admin RNF ne voit que ses users', async ({ adminRnfPage }) => {
await adminRnfPage.goto('/administration/utilisateurs');
});Chaque page a son propre contexte navigateur. On peut donc tester des workflows multi-utilisateurs sans impersonation.
| Page Object | Fichier | Description |
|---|---|---|
LoginPage |
login.page.ts |
Formulaire de connexion |
RegisterPage |
register.page.ts |
Formulaire d'inscription |
HomePage |
home.page.ts |
Page d'accueil avec tuiles |
ProfilePage |
profile.page.ts |
Page profil utilisateur |
AdminLayoutPage |
admin-layout.page.ts |
Sidebar admin + navigation |
AdminUsersPage |
admin-users.page.ts |
Tableau des utilisateurs |
AdminSitesPage |
admin-sites.page.ts |
Tableau des sites |
AdminValidationsPage |
admin-validations.page.ts |
Tableau des validations |
AdminOrganismesPage |
admin-organismes.page.ts |
Grille/détail organismes |
AdminDashboardPage |
admin-dashboard.page.ts |
Dashboard statistiques |
SitesListPage |
sites-list.page.ts |
Liste publique des sites |
| Catégorie | Fichier | Tests | Fonctionnalités couvertes |
|---|---|---|---|
| Auth | login.spec.ts |
5 | Login valide, identifiants invalides, champs vides, returnUrl, lien inscription |
| Auth | logout.spec.ts |
3 | Suppression tokens, redirection, bouton menu |
| Auth | register.spec.ts |
5 | Inscription valide, validation, password mismatch, email doublon |
| Admin Users | users-list.spec.ts |
6 | Liste complète, scope organisme, recherche, filtres rôle/statut |
| Admin Users | users-actions.spec.ts |
5 | Activation/désactivation, assign site, impersonation |
| Admin Users | users-sites.spec.ts |
4 | Site chips, assign modal, référent badge, plan chips |
| Admin Sites | sites-list.spec.ts |
5 | Liste, recherche, filtre type, colonnes, résumé |
| Admin Sites | sites-crud.spec.ts |
5 | Bouton ajout, modal création, validation, rôle-based |
| Admin Sites | sites-orgs.spec.ts |
3 | Org chips, assign org, user chips |
| Admin | validations.spec.ts |
6 | Liste, filtres statut/type, approbation, détail, état vide |
| Admin | validation-workflow.spec.ts |
8 | Workflow multi-utilisateurs complet : création demande → vue admin → approbation/rejet → vérification statut |
| Admin | organismes.spec.ts |
4 | Grille, détail admin_og, recherche, modal édition |
| Admin | dashboard.spec.ts |
3 | Accès, stats cards, message bienvenue |
| Accès | role-access.spec.ts |
8 | Accès super_admin, admin_og, référent, utilisateur, guest |
| Accès | data-scope.spec.ts |
5 | Scope données RNF, CEN, super admin, référent |
| Navigation | navigation.spec.ts |
4 | Header, sidebar rôle, items masqués, navigation sans erreur |
| Total | 17 fichiers | ~80 |
Le test validation-workflow.spec.ts vérifie le flux complet sans utiliser l'impersonation :
1. userRnfPage (utilisateur) → crée une demande d'accès site via API
2. userRnfPage (utilisateur) → vérifie la demande sur /mes-demandes (statut "en attente")
3. superAdminPage (admin) → vérifie la demande sur /admin/validations
4. superAdminPage (admin) → approuve la demande
5. userRnfPage (utilisateur) → vérifie le statut "approuvé"
6. superAdminPage (admin) → vérifie dans l'historique des validations
Un second scénario teste le rejet avec un motif, en utilisant userCenPage et superAdminPage.
Les tests génèrent :
- Rapport HTML :
frontend/playwright-report/(ouvrable localement) - JUnit XML :
frontend/e2e-results.xml(pour CI/CD) - Screenshots :
frontend/test-results/(captures en cas d'échec) - Traces : Enregistrement vidéo + trace réseau en cas de retry
-
Tests E2E avec Playwright ou Cypress✅ Fait (Playwright, ~80 tests) -
Augmenter couverture backend
- ViewSets plans (actuellement 59%)
- Filtres avancés (actuellement 62%)
- Endpoints d'impersonation
-
Tests frontend services
AdminService(actuellement 0%)- Composants modaux (forms, confirmations)
-
Tests de performance
- Benchmarks API avec locust ou k6
- Tests de charge pour endpoints critiques
- Profiling requêtes SQL
-
Tests de sécurité
- Tests d'injection SQL
- Tests XSS
- Validation CORS
- Tests de rate limiting
-
Tests de migration
- Vérifier réversibilité des migrations
- Tests de données avant/après migration
-
Tests de composants Angular
- Composants d'affichage (gauges, icons)
- Composants de navigation
- Formulaires réactifs
-
Documentation automatique
- Génération doc API depuis tests
- Badges de couverture dans README
Le workflow se déclenche sur :
- Push vers
mainoudevelop - Pull request vers
mainoudevelop - Push d'un tag
v*(releases) - Déclenchement manuel (
workflow_dispatch)
| Job | Dépendance | Description |
|---|---|---|
backend-tests |
- | pytest avec PostgreSQL/PostGIS |
frontend-tests |
- | Jest (unitaires Angular) |
typecheck |
- | TypeScript --noEmit |
e2e-tests |
backend-tests |
Playwright avec stack Docker complet |
build |
backend + frontend + typecheck | Build production Angular |
email-tests |
- | Tests email Mailpit (workflow_dispatch uniquement) |
Le job e2e-tests :
- Démarre les services Docker (db, redis, web)
- Attend le health check backend (
/api/auth/health/) - Seed les données de test (
seed_testdata) - Installe Playwright + Chromium
- Démarre le dev server Angular
- Exécute les tests Playwright
- Upload le rapport HTML + JUnit en artifact (14 jours de rétention)
Artefacts CI : Le rapport Playwright est consultable dans l'onglet "Artifacts" de chaque run GitHub Actions.
Les tests de packaging valident l'installation et la mise à jour du package Debian (.deb). Ils sont exclus de la CI pour deux raisons :
- Hyperviseur requis : le test VM utilise Multipass, qui nécessite KVM/QEMU — incompatible avec les runners GitHub Actions
- Durée : 10-20 min par exécution, inapproprié pour un pipeline déclenché à chaque push
Quand les lancer : uniquement avant de publier un nouveau package .deb (release), pour valider que le mécanisme d'upgrade fonctionne.
| Script | Environnement | Durée | Ce qu'il teste |
|---|---|---|---|
test-install-quick.sh |
Conteneur Docker | ~30s | Fichiers installés aux bons emplacements |
test-install.sh |
Conteneur Docker | ~5 min | Fichiers + services systemd + heartbeat |
test-install-web.sh |
Conteneur Docker | ~5 min | Interface web Flask (http://localhost:4567) |
test-install-full.sh |
Conteneur Docker | ~10 min | Installation complète avec systemd |
test-install-web-full.sh |
Conteneur Docker | ~10 min | Interface web + Docker fonctionnel |
test-upgrade-vm.sh |
VM Multipass | 10-20 min | Upgrade v1→v2 : postinst, .env, systemd, docker compose |
cd packaging
# Prérequis pour le test VM
sudo snap install multipass
# Test d'upgrade complet (adapter les versions)
./test-upgrade-vm.sh --from 0.1.12 --to 0.1.13
# Relancer rapidement (réutilise la VM)
./test-upgrade-vm.sh --skip-install --from 0.1.12 --to 0.1.13
# Nettoyer
./test-upgrade-vm.sh --cleanupDocumentation détaillée : packaging/TESTING.md
Ajouter au README.md :

[](https://codecov.io/gh/RNF-SI/Cicada)Créer codecov.yml à la racine :
coverage:
status:
project:
default:
target: 70%
threshold: 5%
patch:
default:
target: 80%
flags:
backend:
paths:
- backend/
carryforward: true
frontend:
paths:
- frontend/
carryforward: true
comment:
layout: "reach,diff,flags,files"
behavior: default
require_changes: true- Nommer clairement :
test_create_user_with_valid_email_succeeds - Un assert par test (quand possible)
- Arrange-Act-Assert pattern
- Utiliser les factories au lieu de créer manuellement
- Isoler les tests : chaque test doit pouvoir s'exécuter seul
- Tests qui dépendent de l'ordre d'exécution
- Données de test en dur (utiliser Faker)
- Tests trop larges qui testent plusieurs fonctionnalités
- Mocks excessifs qui ne testent plus rien de réel
- Exécuter les tests avant chaque commit
- Maintenir la couverture au-dessus de 70%
- Revoir les tests lors des refactorings
- Documenter les tests complexes