
Ecrire un test unitaire simple (`testing`, `go test`)
Apprenez les bases des tests unitaires en Go. Découvrez comment écrire une fonction de test simple avec le paquet `testing` et l'exécuter à l'aide de la commande `go test`.
La qualité par la vérification : Pourquoi tester son code ?
Ecrire du code qui fonctionne est une première étape, mais comment s'assurer qu'il fonctionne *correctement* dans différentes situations et qu'il continue de fonctionner après des modifications ou des ajouts futurs ? C'est là qu'interviennent les tests automatisés. Ils constituent un filet de sécurité essentiel pour garantir la qualité, la fiabilité et la maintenabilité du code.
Parmi les différents types de tests, les tests unitaires sont les plus fondamentaux. Un test unitaire vise à vérifier le bon fonctionnement d'une petite unité isolée de code, typiquement une seule fonction ou méthode, en lui fournissant des entrées spécifiques et en s'assurant que la sortie ou le comportement produit est bien celui attendu.
Go intègre la philosophie du test directement dans son outillage standard. Il fournit un paquet `testing` et une commande `go test` qui rendent l'écriture et l'exécution de tests unitaires simples et directes. Adopter les tests unitaires dès le début est une pratique clé pour tout développeur Go sérieux.
Anatomie d'un fichier de test Go
Les tests unitaires en Go sont placés dans des fichiers spécifiques qui suivent une convention de nommage stricte : si votre code source se trouve dans un fichier nommé `moncode.go`, les tests correspondants doivent se trouver dans un fichier nommé `moncode_test.go`, situé dans le même répertoire (et donc dans le même paquet).
A l'intérieur de ce fichier `_test.go`, vous écrirez des fonctions de test. Ces fonctions doivent également respecter des règles précises :
1. Elles doivent commencer par le préfixe `Test` suivi d'un nom descriptif commençant par une majuscule (par exemple, `TestAdditionSimple`, `TestCalculAireRectangle`).
2. Elles doivent accepter un seul argument : un pointeur de type `*testing.T`. Ce paramètre (souvent nommé `t` par convention) fournit les méthodes nécessaires pour signaler les échecs et enregistrer des informations pendant le test.
3. Elles ne doivent retourner aucune valeur.
Voici la structure de base :
// Fichier: moncode_test.go
package monpaquet // Doit être dans le même paquet que le code à tester
import (
"testing" // Importe le paquet standard pour les tests
)
// Fonction de test pour une fonction hypothétique 'MaFonction'
func TestMaFonctionCasSimple(t *testing.T) {
// 1. Préparation (Setup): Initialiser les données d'entrée
arg1 := ...
attendu := ...
// 2. Exécution (Act): Appeler la fonction à tester
resultat := MaFonction(arg1)
// 3. Vérification (Assert): Comparer le résultat obtenu avec le résultat attendu
if resultat != attendu {
// Signaler une erreur si la vérification échoue
t.Errorf("MaFonction(%v) = %v; attendu %v", arg1, resultat, attendu)
}
}
// Autre fonction de test pour un cas différent
func TestMaFonctionCasLimite(t *testing.T) {
// ... préparation, exécution, vérification ...
}Signaler les échecs : Les méthodes de `*testing.T`
L'objet `*testing.T` (passé en argument `t`) est votre principal outil pour interagir avec le framework de test. Les méthodes les plus couramment utilisées pour signaler un échec sont :
- `t.Errorf(format string, args ...interface{})` : Enregistre un message d'erreur formaté (comme `fmt.Printf`) et marque le test comme échoué, mais continue l'exécution du reste de la fonction de test. C'est utile si vous voulez signaler plusieurs erreurs potentielles dans un seul test.
- `t.Fatalf(format string, args ...interface{})` : Enregistre un message d'erreur formaté et marque le test comme échoué, mais arrête immédiatement l'exécution de la fonction de test actuelle (similaire à `log.Fatalf`). Utile si une condition préalable échoue et que le reste du test n'a plus de sens.
- `t.Fail()` : Marque le test comme échoué mais continue l'exécution. Moins courant que `Errorf`.
- `t.FailNow()` : Marque le test comme échoué et arrête immédiatement son exécution. Moins courant que `Fatalf`.
- `t.Logf(format string, args ...interface{})` : Enregistre un message informatif (qui ne sera affiché que si le test échoue ou si l'option `-v` est utilisée avec `go test`). Utile pour le débogage.
Le choix entre `Errorf` (continuer) et `Fatalf` (arrêter) dépend de la nature de l'échec. Si l'échec empêche la suite du test d'être valide, utilisez `Fatalf`. Si vous voulez vérifier plusieurs assertions indépendantes, utilisez `Errorf` pour chacune.
Exemple concret : Tester une fonction d'addition
Supposons que nous ayons une fonction simple dans `maths.go` :
// Fichier: maths.go
package maths
func Addition(a, b int) int {
return a + b
}
Nous allons écrire un test unitaire dans `maths_test.go` :
// Fichier: maths_test.go
package maths // Même paquet
import "testing"
func TestAddition(t *testing.T) {
// Cas 1: Nombres positifs
resultat1 := Addition(2, 3)
attendu1 := 5
if resultat1 != attendu1 {
t.Errorf("Addition(2, 3) = %d; attendu %d", resultat1, attendu1)
}
// Cas 2: Nombre négatif
resultat2 := Addition(-1, 5)
attendu2 := 4
if resultat2 != attendu2 {
t.Errorf("Addition(-1, 5) = %d; attendu %d", resultat2, attendu2)
}
// Cas 3: Zéro
resultat3 := Addition(0, 0)
attendu3 := 0
if resultat3 != attendu3 {
t.Errorf("Addition(0, 0) = %d; attendu %d", resultat3, attendu3)
}
// Cas 4: volontairement faux pour voir l'échec
resultat4 := Addition(10, 10)
attendu4 := 21 // Erreur volontaire
if resultat4 != attendu4 {
t.Errorf("Addition(10, 10) = %d; attendu %d", resultat4, attendu4)
}
}
Exécuter les tests : La commande `go test`
Une fois vos fichiers `_test.go` écrits, vous pouvez exécuter les tests de votre paquet en utilisant la commande `go test` depuis le répertoire du paquet.
Exécution simple :
cd /chemin/vers/monpaquet
go testLa sortie indiquera si les tests ont réussi ou échoué. Si un test échoue, `go test` affichera le nom du fichier, le numéro de ligne de l'échec et le message d'erreur que vous avez fourni avec `t.Errorf` ou `t.Fatalf`. Pour notre exemple avec l'erreur volontaire :
--- FAIL: TestAddition (0.00s)
maths_test.go:28: Addition(10, 10) = 20; attendu 21
FAIL
FAIL monpaquet 0.123s
FAIL(Le temps et le nom du paquet peuvent varier). Si tous les tests passent, la sortie sera plus succincte, indiquant simplement `ok` :ok monpaquet 0.123sQuelques options courantes pour `go test` :
- `go test -v` : Mode verbeux. Affiche le nom de chaque test exécuté (même ceux qui réussissent) et les messages enregistrés avec `t.Logf`.
- `go test ./...` : Exécute les tests dans le répertoire courant et tous ses sous-répertoires (très utile pour tester tout un projet).
- `go test -run NomDuTest` : Exécute uniquement les tests dont le nom correspond au pattern (expression régulière) fourni. Par exemple, `go test -run Simple` exécuterait `TestAdditionSimple` mais pas `TestCasLimite`.
- `go test -cover` : Calcule et affiche le pourcentage de couverture de code par les tests.
- `go test -coverprofile=coverage.out && go tool cover -html=coverage.out` : Génère un rapport de couverture HTML détaillé montrant quelles lignes de code ont été exécutées par les tests.
Conclusion : Un pilier de la qualité logicielle en Go
Les tests unitaires sont une pratique indispensable pour développer des logiciels robustes et maintenables. Go facilite grandement l'intégration des tests dans le processus de développement grâce à son paquet `testing` intégré et à la commande `go test`.
Retenez les conventions clés :
- Fichiers de test nommés `*_test.go`.
- Fonctions de test nommées `TestXxx(*testing.T)`.
- Utilisation de `t.Errorf` ou `t.Fatalf` pour signaler les échecs.
- Exécution via `go test`.
Commencer à écrire des tests simples pour vos fonctions dès le début vous aidera à détecter les régressions, à clarifier le comportement attendu de votre code et à augmenter votre confiance lors des refactorisations ou des évolutions de votre projet.