Quels problèmes cherche-t-on à résoudre quand on teste notre code ?
L’ingénierie logicielle est plus “empirique” que d’autres disciplines d’ingénierie reposant - par exemple - sur des calculs précis.
Concevoir, développer et maintenir une application fiable et évolutive s’avère complexe, d’autant plus que de nombreuses personnes sont impliquées.
Pour le dire plus simplement : il est difficile de produire des programmes informatiques exempts de bugs.
Tester une application manuellement est compliqué et chronophage : au moindre changement, il faudrait tout retester, car il est difficile d’être certain·e qu’un changement mineur n’ait pas d’impacts imprévus.
On va parler ici de tests automatisés : des tests que l’on va pouvoir effectuer régulièrement afin de s’assurer que notre code fonctionne correctement. Ces tests vont consister en du code spécifique qui va tester le code de l’application proprement dite (ou de ses composants).
Les tests visent l’amélioration de la robustesse et de la qualité des applications, sous différents aspects :
Le TDD ou Test-Driven Development se traduit en développement guidé par les tests. C’est une méthodologie de développement qui implique d’écrire d’abord des tests avant d’écrire le code à tester.
Cette approche comporte plusieurs avantages :
L’approche TDD incite à reconsidérer la définition d’une fonctionnalité terminée (Definition of done) : une fonctionnalité est considérée comme complète lorsqu’elle est implémentée, vérifiée (code review), documentée, intégrée et testée.
Un test unitaire permet de tester une unité telle que :
Nous allons commencer avec des tests unitaires de fonctions en TypeScript. Les outils que nous allons voir ici serviront également pour d’autres types de tests.
Créez un nouveau répertoire
intro-tests-typescript
et ouvrez-le dans votre
IDE.
Placez-vous dans ce dossier et initialisez le projet avec les commandes suivantes :
git init
npm init -y
yarn add --dev typescript @types/node @types/jest ts-node jest ts-jest
npx tsc --init
mkdir src
mkdir test
Dans l’ordre, on a initialisé un dépôt Git, initialisé un
package.json
, installé TypeScript, Jest
(framework de test), initialisé
tsconfig.json
.
Créez un fichier .gitignore
et ajoutez-y node_modules
.
Dans un premier temps, on ne va pas utiliser tous les outils qu’on vient d’installer, mais aborder les tests avec une approche simpliste.
Créez un dossier src
et créez un fichier
math.ts
contenant le code suivant :
// src/math.ts
export const add = (a: number, b: number): number => a + b;
export const sub = (a: number, b: number): number => a - b;
Ce code est volontairement trivial, afin de se concentrer sur les tests.
À quoi sert un test ? À vérifier que le code son écrit se comporte de la façon attendue (expected). Il s’agit donc d’exécuter notre code, et de comparer ses résultats à ce qu’on attend.
Par exemple, si on appelle la fonction
add(2, 2)
, on s’attend en retour à recevoir
4
. Voyons comment traduire cela en code.
Sous test/
, créez un fichier
math.test.ts
, avec ce contenu :
// test/math.test.ts
import assert from "node:assert";
import { add } from "../src/math";
// on va appeler la fonction `add` avec 2 paramètres.
const result = add(4, 5);
// on s'attend à ce que le résultat soit 9.
const expected = 9;
// Si le résultat est différent de l'attendu, la ligne suivante va _throw_ une erreur.
.equal(
assert,
result,
expected`add - Failure! Expected ${expected}, received: ${result}.`
;
)console.log("add - Success!");
Pour lancer ce test, vous pouvez exécuter :
npx ts-node test/math.test.ts
En principe, vous verrez juste s’afficher
Success!
.
Vous pouvez ajouter et committer tout le contenu du répertoire en l’état.
Pour voir comment le test se comporterait, modifiez
temporairement la fonction add
du fichier math.ts
:
// Renvoie volontairement un résultat incorrect
export const add = (a: number, b: number): number => 0;
Relancez : npx ts-node test/math.test.ts
. Vous
devriez obtenir un résultat semblable à ceci :
/home/johndoe/Code/intro-tests-typescript/test/math.test.ts:10
assert.equal(
^
AssertionError [ERR_ASSERTION]: add - Failure! Expected 9, received: 0.
at Object.<anonymous> (/home/johndoe/Code/intro-tests-typescript/test/math.test.ts:10:8)
at Module._compile (node:internal/modules/cjs/loader:1376:14)
at Module.m._compile (/home/johndoe/Code/intro-tests-typescript/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
at Object.require.extensions.<computed> [as .ts] (/home/johndoe/Code/intro-tests-typescript/node_modules/ts-node/src/index.ts:1621:12)
at Module.load (node:internal/modules/cjs/loader:1207:32)
at Function.Module._load (node:internal/modules/cjs/loader:1023:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
at phase4 (/home/johndoe/Code/intro-tests-typescript/node_modules/ts-node/src/bin.ts:649:14)
at bootstrap (/home/johndoe/Code/intro-tests-typescript/node_modules/ts-node/src/bin.ts:95:10) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 0,
expected: 9,
operator: '=='
}
Que nous dit ce long message d’erreur ? Il faut se pencher sur les dernières lignes.
Le code
d’erreur est
ERR_ASSERTION
. Il s’agit d’une erreur
d’assertion. Assertion est synonyme
d’affirmation. En écrivant
assert.equal(result, expected);
, on indique que
result
devrait être égal à
expected
.
La ligne actual
correspond ici à la valeur
renvoyée par add
, et stockée dans
result
.
La ligne expected
correspond à la valeur
attendue.
La valeur réelle (actual
) étant différente de
l’attendue, une erreur a été générée.
Ce test simple illustre un principe général des tests : on va définir nos attentes avec des lignes de code similaires à notre
assert.equal()
. Si le code est correct, l’assertion ou attente définie parassert.equal()
va être satisfaite. Dans le cas contraire, une erreur va être produite.
Remettez le code de add.ts
dans son
état antérieur en renvoyant a + b
au
lieu de 0
.
Le test ci-dessus, bien qu’il fonctionne, n’est pas idéal.
Dans le cas présent, on n’a testé qu’une seule des deux
fonctions de notre module math.ts
.
Si on ajoute des fonctions à ce module et qu’on souhaite toutes les tester, on va devoir :
math.test.ts
, et le structurer (par exemple en
fonctions) pour que chaque test de chaque fonction n’impacte
pas les autres tests.add.test.ts
,
sub.test.ts
), et dans ce cas, écrire une sorte de
“script” pour les lancer tous.De plus, si on se contente de simples messages de succès/erreur comme ci-dessus, il va rapidement devenir difficile de se repérer dans la “sortie” console produite par l’exécution des tests.
En bref, cette façon de faire est beaucoup trop simpliste pour être efficace sur un projet substantiel.
Les méthodologies de test logiciel existent depuis suffisamment longtemps pour que des outils efficaces aient été développés dans tous les langages de programmation.
En JavaScript, les frameworks Mocha et Jasmine sont longtemps restés les plus utilisés. On peut citer également QUnit.
Facebook/Meta a introduit Jest comme framework de test, notamment pour tester React et les applications écrites avec.
Jest est devenu depuis le principal framework de test JavaScript/TypeScript. Mais la communauté continue à produire de nouveaux outils, et des challengers comme vitest ont fait leur apparition.
À quoi servent ces outils ? Ils vont faciliter :
La plupart fournissent des fonctionnalités similaires, avec même certains noms de fonctions identiques.
Nous allons utiliser Jest car c’est le plus complet et le plus utilisé. Nous l’avons déjà installé lors de l’initialisation du projet.
On va continuer à travailler dans le même dossier.
Remplacez le contenu de test/math.test.ts
par
ceci :
import assert from "assert";
import { add } from "../src/math";
test("add should return 5 for 2 + 3", () => {
const expected = 5;
const actual = add(2, 3);
.equal(actual, expected);
assert;
})
test("add should return 9 for 4 + 5", () => {
const expected = 9;
const actual = add(4, 5);
.equal(actual, expected);
assert; })
À noter : Jest se sert de l’extension des fichiers pour détecter les fichiers de tests. Il faut leur donner l’extension
.test.js
ou.spec.js
en JavaScript ;.test.ts
ou.test.ts
en TypeScript.
On rentrera dans les détails un peu plus tard, mais voici déjà quelques indications sur ce qu’apporte l’utilisation de Jest.
Ici, on a écrit deux tests, chacun étant
défini par l’appel à la fonction built-in (intégrée)
test
fournie par Jest.
Cette fonction prend deux arguments :
Un avantage immédiat - par rapport à l’approche “naïve” précédente - est que Jest nous fournit une façon d’écrire plusieurs tests, indépendant les uns des autres. Si l’un échoue ou réussit, cela n’affectera pas les autres.
Nous avons déjà installé toutes les dépendances nécessaires.
Mais si on lance en l’état npx jest
pour
invoquer Jest, on va obtenir une erreur, car il manque un
fichier de configuration pour faire fonctionner Jest avec
TypeScript.
Lancez :
npx ts-jest config:init
Cela va créer un fichier jest.config.js
avec
les des paramètres prédéfinis (presets) pour
TypeScript.
Dans ce fichier, ajoutez la ligne
verbose: true
pour aboutir à ceci :
/** @type {import('ts-jest').JestConfigWithTsJest} */
.exports = {
modulepreset: "ts-jest",
testEnvironment: "node",
verbose: true,
; }
Tant qu’à effectuer un peu de configuration, modifiez la
valeur derrière "test"
dans le
package.json
. Vous aviez jusqu’ici :
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
Modifiez le fichier de façon à obtenir ceci :
"scripts": {
"test": "jest",
"testw": "jest --watch"
},
Grâce à cela, vous pourrez :
npm test
npm run testw
ou yarn testw
Lancez d’abord npm test
. Vous devriez obtenir
:
$ npm test
> code@1.0.0 test
> jest
PASS test/math.test.ts
✓ add should return 5 for 2 + 3 (1 ms)
✓ add should return 9 for 4 + 5
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 1.184 s, estimated 2 s
Ran all test suites.
Vous pouvez déjà remarquer un autre bénéfice par rapport à l’approche naïve initiale : Jest fournit un reporting clair et lisible sur les tests qui ont réussi.
Vous pouvez essayer npm testw
: Jest se lance
et ne quitte pas. Il va attendre que le code et/ou les tests
soient modifiés, et relancer les tests à chaque
modification.
Les tests que nous avons écrits auraient pu fonctionner quasiment tels quels avec un autre framework de test comme Mocha ou Jasmine. Mais Jest fournit des outils que ces derniers n’ont pas, à savoir une bibliothèque d’assertions intégrée.
assert
est la bibliothèque d’assertions livrée
en standard avec Node.js, mais elle est assez limitée. Les
assertions écrites avec les méthodes fournies par ce module ne
sont pas très lisibles ou explicites.
D’autres bibliothèques existent, telles que Chai, qui permet d’écrire d’assertion dans des styles plus proches d’une façon de parler naturelle. Chai est très souvent utilisé avec Mocha et Jasmine.
Il se trouve que Jest fourni sa propre bibliothèque d’assertions.
Dans les deux tests, remplacez :
.equal(actual, expected); assert
Par :
expect(actual).toBe(expected);
Ceci peut se lire ainsi : “on s’attend à ce que le résultat
effectif (actual
) de l’appel de fonction
soit la valeur attendue
(expected
)”.
C’est une façon plus naturelle d’écrire des assertions.
toBe
est un “comparateur” ou matcher. Jest propose un grand nombre de matchers, décrits dans la section Utilisation des comparateurs de sa documentaton.
Exercice 1
Écrivez des tests pour la fonction sub
. Vous
pouvez les écrire dans math.test.ts
, à la suite
des tests existants.
Exercice 2
Implémentez, suivant l’approche TDD, la fonction
mul
.
mul
dans
math.ts
.Des solutions possibles de ces exercices sont proposées ici.
Après les exercices, vos tests doivent ressembler à :
import { add, sub, mul } from "../src/math";
test("add should return 5 for 2 + 3", () => {
// ...
;
})
test("add should return 9 for 4 + 5", () => {
// ...
;
})
test("sub should return 3 for 8 - 5", () => {
// ...
;
})
test("sub should return 4 for 13 - 9", () => {
// ...
;
})
test("mul should return 27 for 9 * 3", () => {
// ...
;
})
test("mul should return 55 for 55 * 11", () => {
// ...
; })
On peut organiser tout cela un peu mieux : par exemple, regrouper ensemble les tests relatifs à une fonction.
Pour cela, nous allons utiliser une autre fonction fournie
par jest
: la fonction describe
, qui
prend également comme arguments un label et une fonction
anonyme.
Voici à quoi pourraient ressembler des tests mieux
organisés. On en profite pour remplacer test
par
it
, ce qui est la même chose - mais plus
fréquemment utilisé et plus “explicite” : “it should return
5…”.
import { add, sub, mul } from "../src/math";
describe("math module", () => {
describe("add function", () => {
it("should return 5 for 2 + 3", () => {
const expected = 5;
const actual = add(2, 3);
expect(actual).toBe(expected);
;
})
it("should return 9 for 4 + 5", () => {
const expected = 9;
const actual = add(4, 5);
expect(actual).toBe(expected);
;
});
})
describe("sub function", () => {
it("should return 3 for 8 - 5", () => {
const expected = 3;
const actual = sub(8, 5);
expect(actual).toBe(expected);
;
})
it("should return 4 for 13 - 9", () => {
const expected = 4;
const actual = sub(13, 9);
expect(actual).toBe(expected);
;
});
})
describe("mul function", () => {
it("should return 27 for 9 * 3", () => {
const expected = 27;
const actual = mul(9, 3);
expect(actual).toBe(expected);
;
})
it("should return 55 for 55 * 11", () => {
const expected = 55;
const actual = mul(11, 5);
expect(actual).toBe(expected);
;
});
}); })
Les describe
peuvent être imbriqués. Outre la
lisibilité des tests eux-mêmes, cela rend le reporting plus
lisible également :
$ yarn test
yarn run v1.22.18
$ jest
PASS test/math.test.ts
math module
add function
✓ should return 5 for 2 + 3 (3 ms)
✓ should return 9 for 4 + 5 (1 ms)
sub function
✓ should return 3 for 8 - 5 (1 ms)
✓ should return 4 for 13 - 9
mul function
✓ should return 27 for 9 * 3 (1 ms)
✓ should return 55 for 55 * 11
Test Suites: 1 passed, 1 total
Tests: 6 passed, 6 total
Snapshots: 0 total
Time: 1.97 s, estimated 3 s
Ran all test suites.
✨ Done in 3.30s.
On n’a pas abordé la fonction div
du module
math.ts
. Elle peut donner lieu à un cas de figure
intéressant : la division par zéro n’est en principe pas
possible !
Si vous exécutez console.log(10/0)
dans la
console d’un navigateur, vous obtenez : Infinity
.
C’est un comportement commun avec d’autres langages conformes
au standard IEEE 754 (arithmétique des nombres à virgule
flottante).
On pourrait décider que notre fonction div
lève une exception plutôt que de renvoyer
Infinity
.
Jest fournit un matcher permettant de s’assurer qu’une fonction renvoie une erreur.
Voici un exemple (vous pouvez l’ajouter dans un fichier de
test, par exemple test/throws.test.ts
, puis
lancer npm test
).
function throwsError() {
throw new Error("An error");
}
it("should throw", () => {
expect(() => {
throwsError();
.toThrow("An error");
}); })
On enveloppe (wrap) l’appel dont on sait qu’il va
lever une exception, dans une fonction fléchée. Jest va
appeler cette fonction fléchée et vérifier qu’elle lève
l’exception (dont on peut optionnellement préciser le message
en argument de toThrow
).
Exercice 3
À partir de cet exemple, écrivez deux tests puis une
implémentation pour div
, suivant cette
spécification :
div(a, b)
doit renvoyer le résultat de la
division de a
par b
.b
passé à
div
vaut zéro, elle doit lever une exception avec
le message Can't divide by zero
.b
valant 0
.b
est non-nul.Solution ici si vous êtes bloqué·e.
Une distinction cruciale, dans les comparateurs Jest, est
celle entre toBe
et toEqual
.
toBe
ne fonctionne que sur des types primitifs (number
,string
,boolean
).
On ne peut pas l’utiliser pour vérifier l’égalité des valeurs de tableaux ou ou d’objets.
Pour cela, il faut utiliser toEqual
. Vous
pouvez créer ces fichiers.
// src/array-utils.ts
export const removeSecondFromArr = (arr: any[]) =>
.filter((it: any, i: number) => i !== 1); arr
// test/array-utils.test.ts
import { removeSecondFromArr } from "../src/array-utils";
it("should remove the second element", () => {
const actual = removeSecondFromArr([1, 2, 3, 5, 7]);
expect(actual).toEqual([1, 3, 5, 7]);
; })
toBeDefined()
,
toBeUndefined()
// src/array-utils.ts (ajouter)
export const findFirstEven = (arr: number[]) =>
.find((n: number) => n % 2 === 0); arr
// test/array-utils.test.ts
import { findFirstEven } from "../src/array-utils";
describe("findFirstEven", () => {
it("should return something", () => {
const actual = findFirstEven([5, 9, 14, 17]);
expect(actual).toBeDefined();
;
})it("should return nothing", () => {
const actual = findFirstEven([5, 9, 13, 17]);
expect(actual).toBeUndefined();
;
}); })
.not
On va ici utiliser un nouveau matcher :
toContain
, qui permet de vérifier qu’un élément
est présent dans un tableau ; et le combiner avec
.not
, qui permet d’inverser une comparaison.
// src/array-utils.ts
export const keepEven = (arr: number[]) =>
.filter((n: number) => n % 2 === 0); arr
// test/array-utils.test.ts
import { keepEven } from "../src/array-utils";
describe("filterEven", () => {
it("result should not contain 1", () => {
const actual = keepEven([1, 2, 4, 6]);
expect(actual).not.toContain(1);
;
})it("result should contain 6", () => {
const actual = keepEven([3, 6, 9]);
expect(actual).toContain(6);
;
}); })
Se familiariser avec les matchers Jest prend un peu de temps ! Il est utile de parcourir la documentation à cette fin.
Les tests vus jusqu’ici étaient synchrones.
Jest permet également de tester du code asynchrone.
Voir cette section de la documentation.
Avec du code asynchrone, le callback fourni à
it
doit :
Promise
async/await
Prenons un exemple pas forcément réaliste : une version
asynchrone de la fonction div
:
// ajouter dans math.ts
export const divAsync = async (a: number, b: number): Promise<number> => {
if (b === 0) {
return Promise.reject(new Error("Can't divide by zero"));
}return Promise.resolve(a / b);
; }
Notez que les callbacks fournis à
it
sont précédés d’async
.
// dans math.test.ts
import { add, sub, mul, div, divAsync } from "../src/math";
describe("math module", () => {
// ... tests existants ...
describe("divAsync function", () => {
it("should resolve a number", async () => {
// ici on "await" pas la promise
const actual = divAsync(24, 4);
// mais on await le expect
await expect(actual).resolves.toBe(6);
;
})
it("should reject", async () => {
// ici on "await" pas la promise
const actual = divAsync(7, 0);
// mais on await le expect
await expect(actual).rejects.toThrow("Can't divide by zero");
;
});
}); })
SkillMatcher
On va aborder des tests un peu plus complexes, sur une classe.
Voici le squelette d’une classe SkillMatcher
et de types associés.
export type Candidate = {
: string;
name: string[];
skills;
}
export class SkillMatcher {
private candidates: Candidate[];
constructor(candidates: Candidate[]) {}
/**
* Renvoie les candidats ayant les compétences demandées
*/
public getCandidatesHavingSkills(skills: string[]): Candidate[] {}
/**
* Renvoie un objet indiquant la "fréquence" des compétences dans la base de candidats.
*
* Par exemple : { HTML: 7, JavaScript: 4, Git: 9 }
*/
public getSkillStats(): Record<string, number> {}
}
Voici comment le code “client” va utiliser cette classe :
const sampleCandidates: Candidate[] = [
{: "Irene Kennedy",
name: ["HTML", "CSS", "React"],
skills,
}
{: "Raul Ramirez",
name: ["HTML", "CSS", "Angular", ".NET"],
skills,
}: "Bobby Diaz", skills: ["C", "C++", "Rust"] },
{ name;
]const matcher = new SkillMatcher(sampleCandidates);
const webCandidates = matcher.getCandidatesHavingSkills(["HTML", "CSS"]);
// devrait renvoyer [
// { name: "Irene Kennedy", ... },
// { name: "Raul Ramirez", ... },
// ]
const stats = matcher.getSkillStats();
// devrait renvoyer { HTML: 2, CSS: 2, React: 1, .... }
Vous allez devoir écrire les tests et les implémentations
pour la classe SkillMatcher
: le constructeur et
les deux méthodes.
Vous pouvez copier le squelette fourni dans
src/skill-matcher.ts
et créer le test dans
test/skill-matcher.test.ts
.
Les tests précédents ont consisté à tester des unités indépendantes : fonctions, classes, etc.
Dans une application complexe, plusieurs composants interagissent entre eux.
Un test d’intégration est un test qui implique plusieurs composants d’une application.
“Composant” est à entendre au sens large :
On va illustrer le concept de tests d’intégration avec des tests de endpoints sur une API Node.js / Express.
Vous allez partir de ce repo de base à forker (puis cloner le fork).
Le repo contient une app Express / TypeScript configurée avec :
sample
et
candidate
,sample
(src/models/sample.ts
).POST /api/samples
qui renvoie une réponse
vide.Il vous restera notamment une dépendance à ajouter, et des routes à implémenter.
La dépendance en question s’appelle supertest
et permet d’effectuer des requêtes sur une app Express, sans
avoir à démarrer le serveur.
Installez-la avec :
yarn add --dev supertest @types/supertest
Vous pouvez créer un fichier
test/app.integration.test.ts
, avec ce contenu
:
// test/app.integration.test.ts
import request from "supertest";
import app from "../src/app";
describe("Home page integration", () => {
it('should send "Hello, World!" if no param is given', async () => {
// Act
const res = await request(app)
.get("/")
// permet de faire une assertion sur le code HTTP de la réponse
.expect(200);
// Assert
expect(res.body).toHaveProperty("message", "Hello, World!");
;
}); })
Explications :
app
qu’a
préalablement importée,GET
sur le endpoint
/
..expect(200)
est déjà une assertion que permet
d’effectuer supertest
.1. Ajouter un 2ème test case pour ces tests d’intégration.
Ajoutez un deuxième test case, vérifiant que la
home page devrait renvoyer Hello, Name!
si un
paramètre est fourni dans l’URL comme ceci :
?name=Name
.
Ensuite, modifiez src/app.ts
pour faire passer les tests.
2. Remettre en place un pipeline d’intégration continue.
Je l’ai supprimé du repo de base : reprenez-en un (ça ne doit pas être dur à trouver), et adaptez-le :
install
,
lint
, build
et ajoutez-une stage
test
au bon endroit3. Écrire des tests d’intégration pour les
opérations CRUD sur la table sample
.
Vous pouvez écrire des tests d’intégration pour les
opérations CRUD sur le routeur samples.ts
.
Pour vous donner quelques lignes directrices :
Vous pouvez regarder les tests du modèle
sample
en regardant
test/models/sample.test.ts
. Cela peut vous donner
des idées sur : - comment nettoyer une base de données (ou une
table spécifique) avant de relancer chaque test. - comment
vérifier que le contenu d’un tableau contient certains
éléments, sans qu’on soit certain de leur ordre. Exemple avec
les lignes mises en évidence sur
le repo.
Référez-vous à la doc de supertest pour des exemples de requêtes en POST, etc.
Vous allez devoir implémenter les endpoints suivants, avec ces “spécifications”.
POST
vers
/api/samples
name
. Exemple :
{ "name": "My sample" }
.name
non-vide), le
endpoint va insérer un objet sample
dans la base
de données en utilisant sampleModel.create
et le
renvoyer, avec un status code 201 (Created).name
absente ou vide), renvoyer une erreur 400
avec un body ayant la forme
{ error: 'Property "name" missing or empty' }
.{ error: '<Erreur renvoyée par SQLite>' }
.
Vous pouvez utiliser un block try/catch et renvoyer
{ error: err.message }
(err
étant
l’erreur “catchée”).PUT
vers
/api/samples/:id
sample
dans la base de
données, comme on le fait icisupertest
, vous allez envoyer
une requête PUT vers le endpoint
/api/samples/<id>
en remplaçant
<id>
par l’id
du sample que
vous venez de créer.POST
.sampleModel.updateOne
, et renvoyer
l’objet mis à jour avec un code de statut 200./api/samples/<id>
avec un id
qui n’existe pas, le modèle sampleModel.updateOne
va renvoyer undefined
. Vous pouvez tester ce cas
pour renvoyer une erreur 404 avec par exemple
{ error: "sample not found" }
.GET
vers
/api/samples/:id
PUT
GET
vers
/api/samples/<id>
en remplaçant
<id>
par l’id
du sample que
vous venez de créer.sampleModel.getOneById
pour récupérer la donnée
en base.{ error: "sample not found" }
.GET
vers
/api/samples
sampleModel.getAll
pour récupérer tous les
samples.4. Répéter la même chose pour le CRUD sur
candidate
.
body
contienne un email valide ; qu’il
contienne un tableau skills
de chaînes de
caractères (qui seront stockés “sérialisés dans la colonne
skills
sous la forme
skill1,skill2,skill3
.name
, email
,
body
) manuellement ou aller un
peu plus loin en utilisant une bibliothèque de validation
comme Express
Validator.Tests pour sub
.
// test/math.test.ts
// on a supprimé l'import d'assert, ajouté sub
import { add, sub } from "../src/math";
// < ... tests pour add ... >
test("sub should return 3 for 8 - 5", () => {
const expected = 3;
const actual = sub(8, 5);
expect(actual).toBe(expected);
;
})
test("sub should return 4 for 13 - 9", () => {
const expected = 4;
const actual = sub(13, 9);
expect(actual).toBe(expected);
; })
Tests pour mul
.
// test/math.test.ts
import { add, sub, mul } from "../src/math";
// < ... tests pour add & sub ... >
test("mul should return 27 for 9 * 3", () => {
const expected = 27;
const actual = mul(9, 3);
expect(actual).toBe(expected);
;
})
test("mul should return 55 for 55 * 11", () => {
const expected = 55;
const actual = mul(11, 5);
expect(actual).toBe(expected);
; })
Implémentation :
// src/math.ts
// ...
export const mul = (a: number, b: number): number => a * b;
Tests pour mul
.
// test/math.test.ts
import { add, sub, mul, div } from "../src/math";
// < ... tests pour add, sub, sub ... >
describe("div function", () => {
test("div should return 7 for 56 / 8", () => {
const expected = 7;
const actual = div(56, 8);
expect(actual).toBe(expected);
;
})
test("div should throw for 5 / 0", () => {
expect(() => {
div(5, 0);
.toThrow("Can't divide by zero");
});
}); })
Implémentation :
// src/math.ts
// ...
export const div = (a: number, b: number): number => {
if (b === 0) {
throw new Error("Can't divide by zero");
}return a / b;
; }