Tests unitaires, débogage et qualité du code
Pourquoi tester ?
Les tests détectent les bugs avant la mise en production, documentent le comportement attendu du code, et permettent de modifier du code existant sans craindre les régressions. Un code sans tests est un code qui se dégrade.
Types de tests
| Type | Scope | Vitesse | Qui ? |
|---|---|---|---|
| Unitaire | Une fonction/méthode isolée | Très rapide | Développeur |
| Intégration | Interaction entre composants (BDD, API) | Moyen | Développeur |
| Fonctionnel | Scénario utilisateur complet | Lent | QA / Dev |
| Non-régression | Vérifier que le code modifié ne casse rien | Variable | CI/CD |
| Performance | Temps de réponse, charge | Lent | Ops / Dev |
Pyramide des tests
La base est large (beaucoup de tests unitaires, rapides et ciblés), le milieu contient des tests d'intégration (moins nombreux), et le sommet des tests E2E/fonctionnels (peu, lents mais critiques).
Tests boîte noire vs boîte blanche
Boîte noire : on teste le comportement sans connaître l'implémentation. On vérifie que pour une entrée donnée, la sortie est correcte. Approche du testeur/QA.
Boîte blanche : on connaît le code et on teste les chemins internes (branches, boucles, cas limites). Approche du développeur. Permet de mesurer la couverture de code.
Tests unitaires en pratique
Structure d'un test (Arrange-Act-Assert)
# pytest (Python)
def test_calculer_moyenne():
# Arrange : préparer les données
notes = [12, 14, 16, 18]
# Act : exécuter la fonction testée
resultat = calculer_moyenne(notes)
# Assert : vérifier le résultat
assert resultat == 15.0// JUnit 5 (Java)
@Test
void testCalculerMoyenne() {
int[] notes = {12, 14, 16, 18};
double resultat = calculerMoyenne(notes);
assertEquals(15.0, resultat, 0.01);
}// PHPUnit (PHP)
public function testCalculerMoyenne(): void
{
$notes = [12, 14, 16, 18];
$resultat = $this->service->calculerMoyenne($notes);
$this->assertEquals(15.0, $resultat);
}Cas limites à tester
Toujours tester : la liste vide, un seul élément, les valeurs limites (0, -1, max), les entrées invalides (null, type incorrect), le cas nominal (fonctionnement normal).
Assertions courantes
| Assertion | Vérifie |
|---|---|
assertEquals(attendu, résultat) | Égalité |
assertTrue(condition) | Condition vraie |
assertNull(valeur) | Valeur nulle |
assertThrows(Exception.class, () -> ...) | Exception levée |
assertContains(élément, collection) | Présence dans une liste |
Mocks et stubs
Quand une classe dépend d'un service externe (BDD, API, email), on la simule pour tester en isolation :
Stub : retourne des valeurs prédéfinies, pas de vérification d'appel.
Mock : simule l'objet ET vérifie que les méthodes attendues ont été appelées avec les bons paramètres.
// PHPUnit — mock du repository
$repo = $this->createMock(ArticleRepository::class);
$repo->method('find')->with(42)->willReturn($articleFictif);
$service = new ArticleService($repo);
$result = $service->getArticle(42);
$this->assertEquals('Mon article', $result->getTitle());Couverture de code
La couverture mesure le pourcentage de code exécuté par les tests. Types : couverture de lignes, de branches (if/else), de fonctions. Un objectif de 80% est courant. Attention : 100% de couverture ne signifie pas zéro bug — la qualité des assertions compte autant.
Débogage
Débogage pas-à-pas
Les IDE modernes (PhpStorm, VS Code, IntelliJ) permettent le débogage interactif : breakpoint (point d'arrêt sur une ligne), step over (exécuter la ligne, passer à la suivante), step into (entrer dans la fonction appelée), step out (sortir de la fonction courante), watch (surveiller la valeur d'une variable en temps réel).
Xdebug (PHP)
; php.ini
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.client_host = host.docker.internal
xdebug.client_port = 9003Techniques de débogage
- Logging :
console.log,var_dump,print— simple mais polluant - Débogueur : breakpoints, inspection — propre et puissant
- Divide and conquer : commenter la moitié du code pour isoler le problème
- Rubber duck : expliquer le code à voix haute (ou à un canard en plastique)
Documentation du code
Commentaires et docblocks
/**
* Calcule la moyenne d'un tableau de notes.
*
* @param float[] $notes Liste de notes (0-20)
* @return float Moyenne arrondie à 2 décimales
* @throws \InvalidArgumentException Si le tableau est vide
*/
public function calculerMoyenne(array $notes): floatConventions de nommage
| Élément | Convention | Exemple |
|---|---|---|
| Classe | PascalCase | ArticleService |
| Méthode/fonction | camelCase | getPublishedArticles() |
| Constante | UPPER_SNAKE | MAX_UPLOAD_SIZE |
| Variable | camelCase | $articleCount |
| Table BDD | snake_case | quiz_sessions |
Points clés pour le concours
- Unitaire = une fonction isolée, Intégration = entre composants
- Boîte noire = tester le comportement, Boîte blanche = tester les chemins internes
- Structure Arrange-Act-Assert pour chaque test
- Toujours tester les cas limites (vide, null, bornes)
- Mocks pour isoler les dépendances externes
- Le débogueur pas-à-pas est plus efficace que
var_dump