Comment exécuter tous les tests dans un projet Go et que se passe-t-il "sous le capot" ?
\nChaque développeur Go a tapé au moins une fois dans sa vie la commande magique go test ./... dans le terminal et a observé les coches vertes défiler sur l'écran. Mais vous êtes-vous déjà demandé ce qui se passe réellement à ce moment-là ? Pourquoi les tests s'exécutent-ils exactement de cette manière et pas autrement ? Et pourquoi parfois go test ignore-t-il silencieusement vos tout nouveaux tests ?
Dans cet article, nous ne répondrons pas simplement à une question populaire sur Stack Overflow (où elle a recueilli des milliers de vues), mais nous regarderons sous le capot du compilateur Go et découvrirons comment fonctionne le mécanisme de test. C'est parti !
\n\nPourquoi go test ./... est-il magique ?
\nCommençons par les bases. La commande go test sans arguments exécute les tests uniquement dans le package actuel. Mais si vous avez un projet avec des dizaines de packages (ce qui est le cas dans un environnement de production réel), les parcourir manuellement équivaut à une torture médiévale. C'est là que ./... vient à la rescousse.
Ce motif signifie : « trouve tous les packages récursivement, en commençant par le répertoire actuel ». Go parcourt l'arborescence des répertoires, en ignorant vendor et les modules, et compile la liste de tous les packages. Pour chacun d'eux, il lance une instance distincte de go test.
Mais que faire si vous voulez exécuter les tests uniquement dans certains répertoires ? Par exemple, uniquement dans ./internal/... ? Cela fonctionne de la même manière : go test ./internal/....
Important : si vous utilisez Go Modules (ce qui est la norme depuis Go 1.16+), ./... ne fonctionne qu'à l'intérieur du module actuel. Pour tester tous les modules d'un workspace, vous devrez utiliser go test ./... dans chacun d'eux ou configurer go.work.
Qu'est-ce qu'un « package » dans le contexte de go test ?
\nGo fonctionne avec la notion de package — un groupe de fichiers .go dans un même répertoire. Les fichiers de test (avec le suffixe _test.go) sont également considérés comme faisant partie du package, mais sont compilés séparément. Lorsque vous lancez go test, Go compile un binaire de test qui inclut :
- \n
- Le code source du package (mais seulement ce qui est utilisé dans les tests) \n
- Le code des tests eux-mêmes \n
- Une couche d'exécution spéciale pour le lancement et le rapport \n
Ce binaire est exécuté, effectue les tests et affiche le résultat. L'ensemble du processus est distinct pour chaque package.
\n\nComment Go décide-t-il quels tests exécuter ?
\nLorsque vous tapez go test ./..., Go effectue les étapes suivantes :
- \n
- Parcours du système de fichiers — Go trouve tous les répertoires contenant des fichiers
.go. \n - Filtrage — les répertoires
vendor,testdataet les dossiers cachés (commençant par.) sont ignorés. \n - Compilation — pour chaque package, un binaire temporaire est créé. \n
- Exécution — le binaire est exécuté, les résultats sont collectés. \n
- Mise en cache — si le code n'a pas changé, Go utilise le cache et affiche
(cached). \n
Ce dernier point est une source fréquente de surprise. Vous avez modifié un test, vous lancez go test ./..., et il affiche « ok (cached) ». Pourquoi ? Parce que Go met en cache les résultats en fonction d'un hachage des fichiers d'entrée. Si vous n'avez modifié qu'un commentaire ou une importation inutilisée, le hachage ne change pas et le test n'est pas relancé.
Astuce : pour vider le cache de force, utilisez la commande go clean -testcache. Ou utilisez le flag -count=1, qui force Go à ignorer le cache : go test -count=1 ./....
Parallélisme : pourquoi les tests ne sont-ils pas toujours plus rapides ?
\nPar défaut, go test exécute les tests au sein d'un même package de manière séquentielle. Mais entre les packages, c'est en parallèle ! Le nombre de packages simultanés est déterminé par le flag -p (par défaut égal au nombre de CPU).
Cela semble génial, mais il y a un hic : e