Aller au contenu principal

Tests unitaires et Gitlab

Introduction

Le but de ce laboratoire est de vous montrer par la pratique ce que nous avons appris à propos de Git, des tests unitaires et de la couverture de code. Le choix de la plateforme Gitlab, parmi les autres plateformes, s'est basé sur le fait que vous n'aurez pas besoin d'installer quoi que ce soit sur votre machine. En effet, Gitlab propose une interface web qui permet de simuler l'envoi de code via Git.

Il est aussi intéressant de signaler que le code est fait en JavaScript et utilise la dernière version de NodeJS. Mais ne vous inquiétez pas ! Le laboratoire a été conçu en tenant compte que vous ne connaissez pas ce langage. Dans le laboratoire, vous devrez corriger des erreurs qui ont été ajoutées. Il vous suffira de lire les commentaires présents dans les fichiers pour supprimer les lignes erronées. En effet, la correction des erreurs devrait être faite par les développeurs.

N'oubliez pas que le "DevOps" n'est pas propre à un langage particulier. Vous auriez très bien pu avoir un laboratoire en Ruby, Java, Go, etc. Mais vous devriez être capables de mettre en place un système de "DevOps" efficace dans tous les cas.

Ce laboratoire sera découpé en étapes plus ou moins difficiles. Si vous prenez le temps de réaliser chaque étape avec attention, vous verrez que les concepts vus au cours seront plus clairs.

Etape 1: création du projet

La première étape consiste à aller sur le site de Gitlab et de vous inscrire. Gitlab est une plateforme gratuite qui propose des services payants pour les sociétés. Pour ce cours, l'offre gratuite sera largement suffisante 😊

Créez un projet à partir de rien ("blank project") :

Choisissez le nom que vous voulez pour le projet. Le "projet slug" se mettra automatiquement à jour en fonction du nom, vous ne devez donc pas y toucher. Vérifiez bien que la visibilité de votre projet est en "privé" et que la case "Initialize repository with a README" est bien cochée.

Etape 2: Création des branches

Créez une branche develop à partir de la branche main. À vous de découvrir comment faire dans Gitlab.

Ensuite, nous allons protéger nos branches. En effet, nous souhaitons que la branche develop et main ne puissent plus être supprimées et que personne ne puisse envoyer du code directement dessus. Pour vous aider, vous trouverez la documentation à cette adresse (vous ne devez pas tout lire, la documentation va beaucoup plus loin que nécessaire) : https://docs.gitlab.com/ee/user/project/protected_branches.html

Maintenant, allez dans le WebIDE.

attention

Vérifiez que vous êtes bien dans la branche develop !

Ajoutez les fichiers suivants :

./package.json
{
"name": "fibonacci",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "vitest run"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@vitest/coverage-istanbul": "^2.1.8",
"vitest": "^2.1.8"
}
}
./vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({})

Maintenant, vous allez créer une nouvelle branche fibonacci à partir de la branche develop.

Assurez-vous d'être sur la nouvelle branche fibonacci et créez le dossier src ainsi que le dossier test Ajoutez les fichiers fibonacci.js (contenant des erreurs qui seront corrigées plus tard) et fibonacci.test.js respectivement dans les dossiers src et test:

./src/fibonacci.js
function fibonacci (nth){
if(nth < 0){
//1. should throw an error
//throw new Error("Argument must be positif");
return 0
}
else if (nth === 0) {
//2. should return 0
//return 0;
return 1
} else if (nth === 1) {
//3. should return 1
//return 1;
return 2;
}

//4. should begin with 0 and 1
//let number1 = 0;
//let number2 = 1;
let number1 = 10;
let number2 = 11;

for(let i = 0; i < nth - 2; i++){
let tmp = number1 + number2;
number1 = number2;
number2 = tmp
}

return number1 + number2;
}

export {fibonacci};
./test/fibonacci.test.js
import {describe, test, expect} from 'vitest';
import {fibonacci} from "../src/fibonacci"

describe('La suite de Fibonacci', () => {
test("sould throw error", () => {
expect(() => fibonacci(-1)).toThrow("Argument must be positif")
})
test("should be 0", () => {
expect(fibonacci(0)).toBe(0);
})
test("should be 1", () => {
expect(fibonacci(1)).toBe(1);
})
test("should be 1", () => {
expect(fibonacci(2)).toBe(1);
})
test("should be 34", () => {
expect(fibonacci(9)).toBe(34);
})
})

Vous devriez obtenir un résultat qui ressemble à ceci:

Etape 3: Merge request

Nous allons créer notre première merge request pour ajouter les modifications de la branche fibonacci à develop. Rendez-vous sur la page de votre repository, dans le menu à gauche, cliquez sur Code pour faire dérouler le menu. Sélectionner "Merge requests" pour arriver sur la page désirée.

Quand vous allez créer la merge request, pensez à bien vérifier que la branche fibonacci est bien la branche source et que la branche develop soit bien la branche cible ! Avant de valider votre merge request, cochez la case "Delete source branch when merge request is accepted.". En effet, cette branche ne nous sera plus utile par la suite et donc nous pouvons la supprimer.

Vous devriez arriver sur une page permettant de réaliser votre merge request.

Félicitation, votre première merge request sur Gitlab est faite !

Exercice 1: notre premier .gitlab-ci.yml

Nous allons enfin mettre en place un système automatique de tests unitaires ! Gitlab utilise des images dockers pour exécuter votre code. Les instructions dans le fichier .gitlab-ci.yml permettent de créer des jobs dans des stages qui sont exécutés (en parallèle, les uns après les autres, sur certaines branches, etc). Vous pouvez utiliser un template "NodeJS" pour vous aider, mais sachez que la plupart des lignes proposées sont inutiles (le laboratoire est beaucoup trop simple pour le système proposé). Vous devrez donc comprendre comment fonctionne ce fichier et pour cela, vous devrez lire un peu de documentation :

  1. https://docs.gitlab.com/ee/ci/ (utile seulement comme introduction sur les concepts)
  2. https://docs.gitlab.com/ee/ci/quick_start/index.html (utile pour cerner le fonctionnement global).
  3. https://docs.gitlab.com/ee/ci/yaml/ (documentation concernant le fichier .gitlab-ci.yml)

Un bon point de départ serait donc de partir du template et de lire la documentation du point 3 pour comprendre le fichier qui vous a été proposé. Ainsi, vous pourrez supprimer les lignes inutiles.

Pour réaliser l'exercice, créez une branche dédiée à l'ajout de ce fichier. N'oubliez pas de fusionner les changements dans develop quand tout fonctionnera.

info

Dans un souci de simplicité, vous devrez exécuter un unique stage nommé test sur toutes les branches sauf la branche main (ce choix serait discutable pour un véritable environnement de production). Vous devez savoir deux choses :

  • Avant de lancer les tests, vous devez avoir installé les modules pour le projet. Ceci est possible via une commande : npm install
  • Pour lancer les tests, vous devez le faire via la commande npm run test

Si votre fichier .gitlab-ci.yml est bien écrit, les tests devraient se lancer automatiquement. Dans votre merge request, vous devriez voir une indication à ce sujet:

En suivant les liens, vous devriez voir les logs de votre pipeline défiler ou, si vous êtes trop lents, voir les logs finaux.

attention

Pour rappel, vous devriez voir des erreurs dans les logs des jobs ! C'est normal !

Si jamais vous désirez voir tous les pipelines de votre projet, il est possible d'avoir la liste complète via le menu de gauche. Cliquez sur "Build", puis sur "Pipelines".

Exercice 2: corriger les erreurs

Il est temps de chasser les erreurs de notre code !

Créez une branche fix-fibonacci et ouvrez le WebIDE à partir de cette branche. Faites une modification pour supprimer une seule erreur.

info

Une ligne qui commence par // est une ligne de commentaires en JavaScript.

Retournez voir les logs et vous devriez voir le nombre d'erreurs diminué. * Répétez cette opération pour corriger les erreurs une par une (allez voir les logs à chaque fois) et vous devriez réussir tous les tests à la fin !

attention

N'oubliez pas que la merge request devra cibler la branche develop.

Exercice 2.5: couverture des tests

On aimerait connaître le pourcentage de code couvert par nos tests. Gitlab nous permet de rajouter cette information directement dans l'interface du site. L'unique condition est de lui préciser comment récupérer cette information depuis les logs.

Créez une branche coverage et faites-y les modifications nécessaires.

info

Un bon point de départ serait de commencer à lire la documentation avec les mots-clés du fichier .gitlab-ci.yml. Vous devriez y trouver les instructions nécessaires et des liens vers des pages complémentaires.

Vous aurez aussi besoin de modifier la configuration de notre framework de tests qui ne calcule aucun coverage pour l'instant. Modifiez le fichier de configuration en utilisant ce code:

./vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
coverage: {
provider: 'istanbul',
include: ["src/**/*.js"],
reportOnFailure: true
},
},
})

et le fichier package.json:

{
"name": "fibonacci",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "vitest run --coverage"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@vitest/coverage-istanbul": "^2.1.8",
"vitest": "^2.1.8"
}
}

Normalement, vous devriez voir ceci si tout fonctionne:

attention

N'oubliez pas que la merge request devra cibler la branche develop.

Exercice 3: le reste du code

Nous souhaitons rajouter deux suites mathématiques à notre projet: celle de Padovan et celle de Syracuse. Commençons par créer la première !

Crééons la branche Padovan et ajoutez le fichier suivant:

./src/padovan.js
function padovan(nth) {
if(nth < 0){
//1. should throw an error
//throw new Error("Argument must be positif");
return 0;
}
else if(nth === 0){
//2. should return 1
//return 1
return 0
} else if (nth === 1){
//3. should return 1
//return 1
return -1;
} else if (nth === 2){
//4. should return 1;
//return 1
return 10;
} else {
//5. should be an addition
//return padovan(nth - 2) + padovan(nth - 3);
return padovan(nth - 2) - padovan(nth - 3);
}
}

export {padovan}

Normalement, vous devriez voir le pourcentage de couverture chuté. À votre avis, pourquoi ?

Maintenant, nous pouvons ajouter le fichier de test:

./test/padovan.test.js
import {describe, test, expect} from 'vitest';
import {padovan} from "../src/padovan";

describe("Padovan sequence", () => {
test("error if negatif argument", () => {
expect(() => padovan(-1)).toThrow("Argument must be positif");
});
test("sould be 1", () => {
expect(padovan(0)).toBe(1);
});
test("sould be 1", () => {
expect(padovan(1)).toBe(1);
});
test("sould be 1", () => {
expect(padovan(2)).toBe(1);
});
test("sould be 1", () => {
expect(padovan(0)).toBe(1);
});
test("sould be 5", () => {
expect(padovan(7)).toBe(5);
});
test("sould be 12", () => {
expect(padovan(10)).toBe(12);
});
test("sould be 16", () => {
expect(padovan(11)).toBe(16);
});
})

Et vous aurez ceci:

N'oubliez pas de faire les corrections nécessaires et normalement vous aurez à nouveau les tests qui fonctionnent:

attention

N'oubliez pas de faire les merge requests nécessaires. Dorénavant, elles ne vous seront plus rappelées.

Créez la dernière branche: syracuse avec les fichiers suivants:

./src/syracuse.js
function syracuse(n, subscript){
if(n < 0 || subscript < 0){
//1. should thrown an error
//throw new Error("Only positif arguments !");
return -1;
}
else if(subscript === 0){
//2. should return the argument "n"
//return n;
return 0;
} else {
if(subscript%2 === 0){
//3. should return the result of syracuse(n, subscript-1)/2
//return syracuse(n, subscript-1)/2;
return syracuse(n, subscript-1)/3
} else {
//4. should return the result of (3*syracuse(n, subscript-1))+1
//return (3*syracuse(n, subscript-1))+1;
return (3*syracuse(n, subscript-1))-1;
}
}
}

export {syracuse};
./test/syracuse.test.js
import {describe, test, expect} from 'vitest';
import {syracuse} from "../src/syracuse";

describe("Syracuse sequence for 15", () => {

test("Error if negatif argument", () => {
expect(() => syracuse(0, -1)).toThrow("Only positif arguments !");
expect(() => syracuse(-1, 0)).toThrow("Only positif arguments !");
});

test("should be 15", () => {
expect(syracuse(15, 0)).toBe(15);
});

test("should be 46", () => {
expect(syracuse(15, 1)).toBe(46);
});

test("should be 23", () => {
expect(syracuse(15, 2)).toBe(23);
});

test("should be 53", () => {
expect(syracuse(15, 6)).toBe(53);
});

})

describe("Syracuse sequence for 7", () => {

test("should be 7", () => {
expect(syracuse(7, 0)).toBe(7);
});

test("should be 22", () => {
expect(syracuse(7, 1)).toBe(22);
});

test("should be 11", () => {
expect(syracuse(7, 2)).toBe(11);
});

test("should be 52", () => {
expect(syracuse(7, 5)).toBe(52);
});

})

Ensuite, n'oubliez pas de faire les corrections.

Exercice 4: vers main et au-delà !

Il ne nous reste plus qu'à envoyer l'ensemble de notre travail sur la branche main ! Après toutes ces merge requests, vous ne devriez avoir aucune difficulté ! On pourrait dire que le laboratoire est terminé...

Il existe un moyen d'afficher des badges sur les pages des projets, comme ici avec le projet Gitlab

Je vous propose de faire pareil avec notre projet ! Il nous faudrait un badge indiquant le pourcentage de coverage (depuis la branche develop) de notre projet. Voici un petit lien qui devrait vous aider: https://docs.gitlab.com/ee/user/project/badges.html

À la fin, vous devriez avoir ceci:

Conclusion

Vous comprenez les bases de l'automatision des tests ainsi que le coverage associé. Vous avez pu visualiser l'importance de ces tests en vous rendant compte que les erreurs apparaissaient avant tout merge dans la branche develop. Ainsi, un développeur peut directement voir si les commits proposés poseront un problème sans avoir regardé le code !

Vous avez terminé (pour de bon) avec le laboratoire ;)