Stratégies de Test
Guide complet pour tester votre intégration avec l'API MONCRENEAU.
1. Tests Unitaires
Tester les fonctions critiques en isolation.
Exemple: Vérification de Signature Webhook
const crypto = require('crypto');
const { verifyWebhookSignature } = require('../lib/webhooks');
describe('verifyWebhookSignature', () => {
const secret = 'your_secret_key';
const payload = JSON.stringify({
id: 'evt_123',
type: 'appointment.created'
});
it('should return true for valid signature', () => {
const signature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const result = verifyWebhookSignature(payload, signature, secret);
expect(result).toBe(true);
});
it('should return false for invalid signature', () => {
const result = verifyWebhookSignature(payload, 'sha256=invalid', secret);
expect(result).toBe(false);
});
it('should return false for missing signature', () => {
const result = verifyWebhookSignature(payload, null, secret);
expect(result).toBe(false);
});
});
Exemple: Gestion des Crédits
const { canCreateAppointment } = require('../lib/credits');
describe('canCreateAppointment', () => {
it('should allow creation with sufficient credits', async () => {
const org = { id: 1, availableCredits: 10 };
const result = await canCreateAppointment(org);
expect(result).toBe(true);
});
it('should block creation with insufficient credits', async () => {
const org = { id: 1, availableCredits: 0 };
const result = await canCreateAppointment(org);
expect(result).toBe(false);
});
});
2. Tests d'Intégration
Tester avec la vraie API.
Configuration
[object Object]
Exemple: Création de Rendez-vous
[object Object]
3. Tests de Webhooks
Option A: Serveur Local avec ngrok
# 1. Installer ngrok
npm install -g ngrok
# 2. Démarrer votre serveur local
npm start # Port 3000
# 3. Exposer avec ngrok
ngrok http 3000
# 4. Configurer l'URL webhook
# https://abc123.ngrok.io/webhooks/moncreneau
Option B: Tests Unitaires avec Mocks
const request = require('supertest');
const app = require('../app');
const crypto = require('crypto');
describe('Webhook Endpoint', () => {
const secret = process.env.WEBHOOK_SECRET;
function signPayload(payload) {
const signature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${signature}`;
}
it('should process appointment.created event', async () => {
const payload = JSON.stringify({
id: 'evt_test_123',
type: 'appointment.created',
created: new Date().toISOString(),
data: {
object: {
id: 123,
status: 'pending',
userPhone: '+224622000000'
}
}
});
const signature = signPayload(payload);
const response = await request(app)
.post('/webhooks/moncreneau')
.set('Content-Type', 'application/json')
.set('X-Moncreneau-Signature', signature)
.set('X-Moncreneau-Event', 'appointment.created')
.send(payload);
expect(response.status).toBe(200);
// Vérifier que l'événement a été traité
const processed = await db.processedEvents.findOne({
eventId: 'evt_test_123'
});
expect(processed).toBeTruthy();
});
it('should reject invalid signature', async () => {
const payload = JSON.stringify({ id: 'evt_test' });
const response = await request(app)
.post('/webhooks/moncreneau')
.set('X-Moncreneau-Signature', 'sha256=invalid_signature')
.send(payload);
expect(response.status).toBe(401);
});
it('should be idempotent', async () => {
const payload = JSON.stringify({
id: 'evt_test_456',
type: 'appointment.created'
});
const signature = signPayload(payload);
// Premier appel
await request(app)
.post('/webhooks/moncreneau')
.set('X-Moncreneau-Signature', signature)
.send(payload);
// Deuxième appel (doublon)
const response = await request(app)
.post('/webhooks/moncreneau')
.set('X-Moncreneau-Signature', signature)
.send(payload);
expect(response.status).toBe(200);
// Vérifier qu'il n'y a qu'un seul enregistrement
const count = await db.processedEvents.countDocuments({
eventId: 'evt_test_456'
});
expect(count).toBe(1);
});
});
4. Tests de Charge
Vérifier que votre intégration peut gérer le volume de production.
Avec Artillery
[object Object]
# Exécuter le test
artillery run load-test.yml
Avec k6
[object Object]
# Exécuter
k6 run load-test.js
5. Tests de Contrat (Contract Testing)
Vérifier que votre code respecte le contrat API.
Avec Pact
[object Object]
6. Tests End-to-End
Scénario complet utilisateur.
describe('E2E: Complete Appointment Flow', () => {
it('should complete full appointment cycle', async () => {
// 1. Récupérer les créneaux disponibles
const slotsResponse = await api.get('/departments/5/availability', {
params: {
startDate: '2026-02-01',
endDate: '2026-02-28'
}
});
expect(slotsResponse.status).toBe(200);
const slot = slotsResponse.data.availability[0].slots[0];
// 2. Créer le rendez-vous
const createResponse = await api.post('/appointments', {
departmentId: 5,
dateTime: slot.dateTime,
name: 'Jean Test'
});
expect(createResponse.status).toBe(201);
const appointmentId = createResponse.data.id;
// 3. Vérifier le statut
const statusResponse = await api.get(`/appointments/${appointmentId}`);
expect(statusResponse.data.status).toBe('pending');
// 4. Annuler le rendez-vous
const cancelResponse = await api.delete(`/appointments/${appointmentId}`);
expect(cancelResponse.status).toBe(200);
// 5. Vérifier l'annulation
const finalStatus = await api.get(`/appointments/${appointmentId}`);
expect(finalStatus.data.status).toBe('cancelled');
});
});
Bonnes Pratiques
✅ À Faire
- Nettoyer les données de test après chaque test
- Tester les cas d'erreur (pas seulement le happy path)
- Automatiser les tests dans la CI/CD
- Tester l'idempotence des webhooks
- Mesurer la couverture de code (> 80%)
- Utiliser des données de test cohérentes
❌ À Éviter
- Hardcoder les IDs de test
- Ignorer les tests qui échouent
- Tester uniquement le succès
- Oublier de tester les timeouts
- Dépendre de données externes instables
Checklist Tests
Avant le déploiement:
- Tests unitaires passent (> 80% coverage)
- Tests d'intégration avec API OK
- Tests de webhooks avec ngrok OK
- Tests de charge validés (100 req/min)
- Tests E2E complets OK
- Tous les cas d'erreur testés
- Documentation des tests à jour
- CI/CD exécute les tests automatiquement