Workflow TDD
Le mode TDD (Test-Driven Development) d'ATOOX inverse le pipeline : les tests sont écrits AVANT le code.
Philosophie
┌─────────────────────────────────────────────────────────────┐
│ CYCLE TDD │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ │
│ │ RED │ ← Écrire un test qui échoue │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ GREEN │ ← Écrire le code minimal qui passe │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ REFACTOR │ ← Améliorer sans casser les tests │
│ └────┬─────┘ │
│ │ │
│ └──────────────────────────────────────────→ RED │
│ │
└─────────────────────────────────────────────────────────────┘
Lancer le mode TDD
/atoox --tdd "ajouter fonction de calcul de TVA"
Pipeline TDD
Le mode --tdd réordonne les steps :
Pipeline Standard :
00c → 00 → 00-init → 00b → 01 → 02 → 03 → 04 → 05 → 06 → 07 → 08 → 09
Pipeline TDD :
00c → 00 → 00-init → 00b → 01 → 02 → 07 ← (tests d'abord)
↓
03 → 08 → 04 → 05 → 06 → 09
Exemple pratique
Étape 1 : Spécification
/atoox --tdd "calculer le prix TTC à partir du prix HT et du taux de TVA"
ATOOX clarifie :
Feature: TVA Calculator
Description: Calculate price including VAT from net price and VAT rate
Acceptance Criteria:
- Calculate TTC = HT × (1 + TVA/100)
- Handle standard rates: 20%, 10%, 5.5%, 2.1%
- Round to 2 decimal places
- Throw error for negative prices
- Throw error for invalid VAT rates
Étape 2 : Tests d'abord (RED)
ATOOX écrit les tests AVANT le code :
// src/lib/tva.test.ts
import { describe, it, expect } from 'vitest';
import { calculateTTC } from './tva';
describe('calculateTTC', () => {
// Happy path
it('should calculate TTC with 20% VAT', () => {
expect(calculateTTC(100, 20)).toBe(120);
});
it('should calculate TTC with 5.5% VAT', () => {
expect(calculateTTC(100, 5.5)).toBe(105.5);
});
it('should round to 2 decimal places', () => {
expect(calculateTTC(99.99, 20)).toBe(119.99);
});
// Edge cases
it('should handle zero price', () => {
expect(calculateTTC(0, 20)).toBe(0);
});
it('should handle zero VAT', () => {
expect(calculateTTC(100, 0)).toBe(100);
});
// Error cases
it('should throw for negative price', () => {
expect(() => calculateTTC(-100, 20)).toThrow('Le prix doit être positif');
});
it('should throw for negative VAT', () => {
expect(() => calculateTTC(100, -5)).toThrow('Le taux de TVA doit être positif');
});
it('should throw for VAT > 100', () => {
expect(() => calculateTTC(100, 150)).toThrow('Le taux de TVA ne peut pas dépasser 100%');
});
});
Exécution des tests :
Running tests...
✗ calculateTTC › should calculate TTC with 20% VAT
Error: calculateTTC is not a function
✗ 8 tests failed
RED phase complete ✓
Étape 3 : Code minimal (GREEN)
ATOOX écrit le code pour faire passer les tests :
// src/lib/tva.ts
export function calculateTTC(priceHT: number, vatRate: number): number {
// Guard clauses
if (priceHT < 0) {
throw new Error('Le prix doit être positif');
}
if (vatRate < 0) {
throw new Error('Le taux de TVA doit être positif');
}
if (vatRate > 100) {
throw new Error('Le taux de TVA ne peut pas dépasser 100%');
}
// Calculation
const priceTTC = priceHT * (1 + vatRate / 100);
// Round to 2 decimal places
return Math.round(priceTTC * 100) / 100;
}
Exécution des tests :
Running tests...
✓ calculateTTC › should calculate TTC with 20% VAT
✓ calculateTTC › should calculate TTC with 5.5% VAT
✓ calculateTTC › should round to 2 decimal places
✓ calculateTTC › should handle zero price
✓ calculateTTC › should handle zero VAT
✓ calculateTTC › should throw for negative price
✓ calculateTTC › should throw for negative VAT
✓ calculateTTC › should throw for VAT > 100
8 tests passed
GREEN phase complete ✓
Étape 4 : Refactoring
ATOOX propose des améliorations :
// src/lib/tva.ts (refactorisé)
/** Standard French VAT rates */
export const VAT_RATES = {
STANDARD: 20,
INTERMEDIATE: 10,
REDUCED: 5.5,
SUPER_REDUCED: 2.1,
} as const;
export type VATRate = typeof VAT_RATES[keyof typeof VAT_RATES];
/**
* Calculate price including VAT (TTC) from net price (HT)
*
* @param priceHT - Net price without VAT
* @param vatRate - VAT rate as percentage (e.g., 20 for 20%)
* @returns Price including VAT, rounded to 2 decimal places
* @throws Error if price is negative
* @throws Error if VAT rate is invalid
*/
export function calculateTTC(priceHT: number, vatRate: number): number {
validateInputs(priceHT, vatRate);
return roundToDecimals(priceHT * (1 + vatRate / 100), 2);
}
function validateInputs(price: number, rate: number): void {
if (price < 0) {
throw new Error('Le prix doit être positif');
}
if (rate < 0) {
throw new Error('Le taux de TVA doit être positif');
}
if (rate > 100) {
throw new Error('Le taux de TVA ne peut pas dépasser 100%');
}
}
function roundToDecimals(value: number, decimals: number): number {
const factor = Math.pow(10, decimals);
return Math.round(value * factor) / factor;
}
Re-exécution des tests :
8 tests passed
REFACTOR phase complete ✓
Tests still green after refactoring
Avantages du mode TDD
| Aspect | Bénéfice |
|---|---|
| Clarté | Les tests définissent le comportement attendu |
| Confiance | Chaque ligne de code est couverte |
| Design | Le code est naturellement testable |
| Documentation | Les tests servent de documentation vivante |
| Régression | Détection immédiate des bugs |
Combinaisons utiles
# TDD + Full Quality
/atoox --tdd --full "implémenter panier d'achat"
# TDD + Pair Programming
/atoox --tdd --pair "ajouter système de notation"
# TDD + Security Audit
/atoox --tdd --security "implémenter authentification"
Bonnes pratiques
1. Un test à la fois
Ne pas écrire tous les tests d'un coup. Cycle : 1 test → code → refactor → prochain test.
2. Tests expressifs
// ❌ Mauvais
it('test1', () => {
expect(fn(100, 20)).toBe(120);
});
// ✅ Bon
it('should calculate TTC with 20% VAT', () => {
const netPrice = 100;
const vatRate = 20;
const expectedTTC = 120;
expect(calculateTTC(netPrice, vatRate)).toBe(expectedTTC);
});
3. Arrange-Act-Assert
it('should apply discount before VAT', () => {
// Arrange
const netPrice = 100;
const discount = 10;
const vatRate = 20;
// Act
const result = calculateDiscountedTTC(netPrice, discount, vatRate);
// Assert
expect(result).toBe(108); // (100 - 10) × 1.20
});
4. Edge cases d'abord
Les cas limites révèlent souvent des bugs de design :
describe('edge cases', () => {
it('should handle zero', () => {});
it('should handle negative', () => {});
it('should handle very large numbers', () => {});
it('should handle decimals', () => {});
it('should handle null/undefined', () => {});
});
Prochaines étapes
- Créer un projet — Mode Genesis complet
- Skills de test — atoox-test-pro
- Configuration — Personnaliser TDD