Loupe

Docker : exécution des tests depuis Azure Pipelines

Que faire après avoir réussi à builder son conteneur ?

Après avoir automatisé la Build de son conteneur (en suivant l’article Optimize ASP.NET Core SPA container with Visual Studio par exemple), vous vous demandez maintenant comment automatiser l’exécution de ses tests depuis Azure Pipelines ?

Voyons ensemble 2 manières d’y arriver :

Le plus simple : exécuter les tests durant la génération de l'image !

La première manière est aussi la plus intuitive : comme pour une Build non Docker, on peut exécuter les tests directement après la compilation, en rajoutant l’instruction dotnet test dans le Dockerfile. Après la génération de l’image, on pourra alors récupérer les résultats de tests depuis le conteneur via la commande docker cp, pour ensuite les uploader sur Azure Pipelines.

Bien que cette technique soit plutôt facile a mettre en place, il y a 2 points qui me chagrinent :

  • Avoir les résultats de tests dans mon image de production (afin de pouvoir les extraire),
  • Et être dans l’incapacité de découpler l’exécution de la compilation de l’exécution des tests.

Enfin tout ça c’était avant que j’entende parler des builds multi-stage de Docker, et surtout de la capacité à cibler unstage en particulier. Voyons ça plus en détails :

Génération de plusieurs images avec le même fichier Dockerfile grâce aux builds multistage

Les builds multistage sont déjà utilisées dans le template Docker de Visual Studio, notamment pour permettre d'obtenir une image de production “propre”, non polluée par tous les outils nécessaires à la génération des binaires de l’application.
Mais je n’étais encore au courant que l’on pouvait cibler un stage particulier afin d’arrêter la Build à la fin de ce stage !

Avec cette fonctionnalité en tête, on est maintenant capable de lancer 2 Build Docker :

  • Une plutôt courte, avec juste le nécessaire pour exécuter les tests automatisés
  • Et une seconde avec tout le packaging et nettoyage requis pour générer une image de production propre

Cerise sur le gâteau : grâce au système de cache de Docker, toutes les étapes effectuées durant la première Build seront rejouées instantanément durant la seconde Build, Docker détectant le contexte de chaque étape de Build, et surtout si une modification a été effectué depuis.

En résumé, on obtient un workflow global :

  • Builder une image ciblant le stage “Test”
  • Exécuter le conteneur, déclenchant ainsi l’exécution des tests
  • Copier les résultats de tests en dehors du conteneur
  • Les publier sur Azure Pipelines et faire échouer la Build en cas d’erreur
  • Si tout se passe bien, poursuivre normalement sur la Build de production et pousser l’image sur le registre

01-multi-stage-docker-build-with-azure-pipelines.png

Ajouter un stage de Test dans le Dockerfile pour exécuter les tests

Reprenons notre Dockerfile existant, et juste avant l’étape de dotnet publish, ajoutons un nouveau stage:

FROM build AS test
WORKDIR /src
ENTRYPOINT ["dotnet", "test", "--logger:trx", "--results-directory", "/testsresults"]

Cette instruction va exécuter la commande dotnet test au moment où le conteneur sera démarré, et produira des résultats au format .trx à l’interieur du dossier /testsresults du conteneur.

On pourra ensuite utiliser les lignes de commande suivantes pour builder notre conteneur en ciblant le stage de test, et ensuite exécuter le conteneur (ce qui aura pour effet de démarrer l’exécution des tests), pour finir par copier les résultats de test dans le répertoire courant.

docker build -t myimage-test --target test .
docker run -i --name mycontainer-test myimage-test
docker cp mycontainer-test:/testsresults .

02-run-tests-from-docker.png

Il ne reste plus qu’à publier les fichiers .trx dans Azure Pipelines, permettant d’avoir un historique des résultats de tests et de faire échouer la build en cas d'échec.

03-run-docker-tests-from-azure-pipelines.png

Pour builder l’image de production, il suffit juste d’utiliser le Dockerfile sans cibler de stage spécifique (NB: le Dockerfile généré par Visual Studio, celui-ci permet déjà d’avoir une image de production “propre” :))

Optimiser le cache Docker en stockant les résultats de test en dehors du contexte de build

Jusqu’à présent, les résultats sont stockés directement dans le workspace de build, ce qui est plutôt pratique, mais empêche Docker de réutiliser son cache durant l’étape de COPY . . alors qu’aucune modification de code n’a eu lieu.

Pour empêcher ce comportement, on peut par exemple stocker les résultats de test en dehors de notre workspace de build, plus précisément à un niveau de dossier “plus haut”.
Pour cela on peut modifier la ligne de copie comme ci-dessous :

docker cp mycontainer-test:/testsresults ../

Et ensuite modifier la propriété Search folder de la tâche Publish Tests Results, pour chercher les résultats dans le dossier $(Agent.BuildDirectory) correspondant au dossier parent de l’endroit où est récupéré le code source.

04-store-tests-results-outside-the-workspace-to-optimize-docker-cache.png

Pour aller plus loin

Cet article couvre principalement la mise en place de tests automatisés pour une application dotnet, mais on peut très bien étendre le principe pour exécuter les tests JavaScript également, en suivant grosso modo des étapes du genre :

  • Ajouter un framework de test tel que jest, ainsi qu’un reporter produisant des résultats au format JUnit comme jest-junit
  • Plutôt que d’utiliser la ligne de commande dotnet test en tant qu’ENTRYPOINT, on pourrait plutôt passer par un script qui permettra d’exécuter à la fois les tests dotnet et les tests JavaScript.
  • On aura également besoin d’ajouter une autre tâche de Publish Tests Results, cette fois-ci configurée pour le format JUnit.

J’espère que ce petit article vous aura donné quelques idées sur la manière de gérer vos tests automatisés avec vos conteneurs Docker.

Comme toujours, vous pourrez trouver un exemple sur Github

Pour le prochain article, je montrerai un exemple pratique d’ajout du support du https dans un conteneur Docker aspnetcore, étant donné que le sujet n’est pas toujours très trivial.

Que le code soit avec vous !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus