Tests d’API, fichier .http et… couverture de code !

Connaissez-vous le format de fichier .http ? C’est un simple fichier texte, lié à aucune technologie en particulier, qui décrit des requêtes HTTP et permet de les exécuter ! Il a plusieurs avantages : il évite les services type Postman, il utilise une syntaxe simple et est facile à lire et à écrire. De plus, il a des intégrations plus ou moins avancées avec les IDE les plus populaires du marché : IntelliJ, Visual Studio et VSCode. Il est également possible d’exécuter les requêtes en CLI…

Problématique

C’est plutôt bien mais il m’est venu une question simple : comment utiliser ces fichiers pour tester mes API et augmenter la couverture de code sans coder des tests d’intégration plus ou moins complexes ?

Les tests d’API (avec les tests de composants, les tests de contrat, etc.) sont une composante des tests d’intégration automatisés en mode « Black Box Testing » ou « Grey Box Testing », proche des tests d’acceptation et E2E. La plupart du temps, ils ne permettent pas de collecter la couverture de code et c’est cette partie qui va nous intéresser par la suite.

Retour en arrière

Avec l’arrivée de .Net 8, les templates WebApi ont vu l’apparition d’un nouveau fichier à côté du fichier de projet : le fichier .http. Ce fichier permet de collecter les différentes requêtes de l’application et de les jouer directement depuis l’IDE. Les développeurs enrichissent ce fichier, en ajoutant de nouveaux et exécutent les requêtes en local.

Fichier .http

Utile ou inutile ?

Mais comment augmenter l’utilité de ces fichiers ? Pourquoi ne pas les exécuter dans les pipelines de compilation sous Azure DevOps ou GitHub Actions, etc.

Beaucoup de développeurs se sont penchés sur le sujet, et avec les outils en ligne de commande, il est relativement facile d’exécuter ces tests d’API.

Malheureusement, dans beaucoup de technologies, les exécuter ne permet pas de collecter la couverture de code par défaut. Une solution envisageable est de continuer à coder des tests d’intégration dans le langage de l’application, quitte à avoir des doublons de tests. Mais dans ce cas, peut-être que les fichiers .http ne sont pas si utiles que ça ?

Quelles solutions ?

Exécuter les fichiers .http en CLI et CI/CD

La première chose à faire est de pouvoir exécuter des tests d’API via les fichiers .http directement en ligne de commande. Pour cela, l’outil qui me semble le plus approprié pourrait être httpyac. Il offre une grande compatibilité avec les intégrations des principaux IDE et permet également de vérifier les informations des réponses HTTP via des assertions simples ou plus complexes.

httpyac est un outil écrit en Typescript compatible à 80-90% avec ijhttp de Jetbrains et à 100% avec VSCode Rest Client. Il ajoute cependant beaucoup de fonctionnalités par rapport à ce dernier.

Pour exécuter httpyac, qui est un outil en ligne de commande, après l'avoir installer via NPM, YARN ou PNPM, il faut taper dans un terminal :

httpyac send <fichier(s) .http> --all

Il est possible de passer des variables d’environnement, d’utiliser des fichiers .env et même plusieurs fichiers en même temps (avec l’utilisation des « glob patterns »).

L’option « --all » permet de demander à l’outil d’exécuter toutes les requêtes du ou des fichiers sans intervention humaine.

Instrumenter les binaires et collecter la couverture de code

Maintenant, pour collecter la couverture de code, il faut pouvoir instrumenter l’exécutable, en l’occurrence les modules .Net, sans utiliser de framework de tests… Plus facile à dire qu’à faire !? Certainement.

J’avais d’abord tenté l’expérience avec coverlet qui est largement utilisé avec les tests unitaires mais il existe encore des problématiques lors de l’exécution en mode CLI. Cette solution n’a pas fonctionné mais m’a permis de trouver un autre outil.

Heureusement, Microsoft fournit un outil nommé dotnet-coverage. Celui-ci permet de lancer l’exécutable de l’application en activant l’instrumentation et en collectant la couverture de code, sans framework de tests « standards », type MSTest, NUnit, xUNit, TUnit ou la nouvelle Testing Platform.

Pour java/kotlin, il semblerait que les équivalents de dotnet-coverage soient jacococli et kotlinx-kover en mode « offline » ; par contre, je n’ai pas trouvé d’équivalent pour les backend en Node/JS/TS ni en Python.

Pour exécuter dotnet-coverage, qui est également un outil en ligne de commande, après installation via dotnet CLI, il est nécessaire de taper plusieurs commandes dans un terminal, en Powershell :

$sid = New-Guid
dotnet-coverage collect --session-id $sid --server-mode --background -o .\Coverage\cobertura.xml -f cobertura --include-files .\WebApi\bin\Debug\net9.0\*.dll
dotnet-coverage connect --background $sid .\WebApi\bin\Debug\net9.0\WebApi.exe

ou en Bash :

SID=$(< /proc/sys/kernel/random/uuid)
dotnet-coverage collect --session-id $SID --server-mode --background -o ./Coverage/cobertura.xml -f cobertura --include-files ./WebApi/bin/Debug/net9.0/*.dll
dotnet-coverage connect --background $SID ./WebApi/bin/Debug/net9.0/WebApi.exe

La première ligne permet de générer un identifiant aléatoire à chaque exécution et de le réutiliser dans chaque commande.

Le seconde ligne démarre un serveur de collection de la couverture de code en arrière-plan (ce qui permet de poursuivre avec les commandes suivantes). On a également préciser le chemin vers le fichier de sortie et son type (cobertura) et les fichiers à instrumenter (*.dll du répertoire de sortie de la compilation).

La dernière ligne lance l’exécutable (WebApi.exe, « exe » étant l’extension de l’exécutable en .Net, même sous Linux), toujours en arrière-plan, après s’être connecté (grâce à l’identifiant de session) au serveur démarré précédemment.

L’instrumentation est en cours d’exécution !

On peut maintenant lancer l’exécution de httpyac comme vu dans le paragraphe précédent dans la même console, idéal pour une pipeline CI/CD.

Lorsque les requêtes ont été exécutées, il est nécessaire de terminer la collection des données avec cette commande (il faut remplacer $sid par $SID sous Bash) :

dotnet-coverage shutdown $sid

Le fichier contenant les informations de couverture de code est désormais créé.

Bonus

Fusionner plusieurs fichiers de couverture de code

Si vous avez plusieurs fichiers générés via httpyac ET les tests unitaires, il est possible de les concaténer en un seul fichier avec cette commande :

dotnet-coverage merge -o merged.cobertura.xml -f cobertura **\*.coverage

Tâches pour Azure DevOps

Voici un extrait d'une pipeline Azure DevOps avec les outils intégrés :

pool:
  vmImage: ubuntu-latest
steps:
[...]
- script: npm install -g httpyac
  displayName: Install httpyac
- script: dotnet tool install -g dotnet-coverage
  displayName: Install dotnet-coverage
- script: |
    SID=$(< /proc/sys/kernel/random/uuid)
    dotnet-coverage collect --session-id $SID --server-mode --background -o .\Coverage\cobertura.xml -f cobertura --include-files .\WebApi\bin\Debug\net9.0\*.dll
    dotnet-coverage connect --background $SID .\WebApi\bin\Debug\net9.0\WebApi.exe
    httpyac send *.http –all
    dotnet-coverage shutdown $SID
  displayName: API tests

Tâches pour GitHub Actions

Et voici un extrait d'une pipeline GitHub Actions :

jobs:
  scripts:
    runs-on: ubuntu-latest
    steps:
    - name: Install httpyac
      run: npm install -g httpyac
    - name: Install dotnet-coverage
      run: dotnet tool install -g dotnet-coverage
    - name: API tests
      run: |
        SID=$(< /proc/sys/kernel/random/uuid)
        dotnet-coverage collect --session-id $SID --server-mode --background -o .\Coverage\cobertura.xml -f cobertura --include-files .\WebApi\bin\Debug\net9.0\*.dll
        dotnet-coverage connect --background $SID .\WebApi\bin\Debug\net9.0\WebApi.exe
        httpyac send *.http –all
        dotnet-coverage shutdown $SID

Conclusion

Les fichiers .http permettent donc d’exécuter des tests d’API de « haut niveau » et de collecter la couverture de code simplement. Plus besoin d’écrire des tests d’intégration plus ou moins complexes.

Passez aux tests d’API avec les fichiers .http : l’essayer c’est l’adopter !