Dans l'article précédent, nous avons examiné les aspects théoriques. Il est temps de commencer à pratiquer.

Faisons une implémentation simple de la pile en JavaScript en utilisant le développement par le biais de tests.
Stack - une structure de données organisée selon le principe de LIFO: Last In, First Out. Il y a trois opérations principales sur la pile:
pousser : ajouter un élément
pop : supprimer un élément
coup d'Ćil : ajouter un Ă©lĂ©ment de tĂȘte
Créez une classe et appelez-la Stack. Pour compliquer la tùche, supposons que la pile ait une capacité fixe. Voici les propriétés et les fonctions d'implémentation de notre pile:
items : objets sur la pile. Nous utiliserons un tableau pour implémenter la pile.
capacité :
capacité de la pile.
isEmpty () : retourne vrai si la pile est vide, sinon faux.
isFull () : renvoie true si la pile atteint sa capacité maximale, c'est-à -dire lorsque vous ne pouvez pas ajouter un autre élément. Sinon, renvoie false.
push (élément) : ajoute un élément. Renvoie Full si la pile est pleine.
pop : supprime l'élément. Renvoie Vide si la pile est vide.
peek () : ajoute un élément head.
Nous allons créer deux fichiers
stack.js et
stack.spec.js . J'ai utilisé l'extension
.spec.js parce que j'y suis habitué, mais vous pouvez utiliser .test.js ou lui donner un nom différent et le déplacer vers
__tests__ .
Puisque nous pratiquons le développement par le biais de tests, nous écrirons un test ayant échoué.
Tout d'abord, vérifiez le constructeur. Pour tester le fichier, vous devez importer le fichier de pile:
const Stack = require('./stack')
Pour ceux qui souhaitent savoir pourquoi je n'ai pas utilisé l'
importation ici, la derniÚre version stable de Node.js ne prend pas en charge cette fonctionnalité aujourd'hui. Je pourrais ajouter Babel, mais je ne veux pas vous surcharger.
Lorsque vous testez une classe ou une fonction, exécutez un test et décrivez le fichier ou la classe que vous testez. Il s'agit de la pile:
describe('Stack', () => { })
Ensuite, nous devons vérifier que lorsque la pile est initialisée, un tableau vide est créé et nous définissons la capacité correcte. Nous écrivons donc le test suivant:
it('Should constructs the stack with a given capacity', () => { let stack = new Stack(3) expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) })
Notez que nous utilisons
toEqual et n'utilisons pas
toBe pour
stack.items , car ils ne font pas rĂ©fĂ©rence au mĂȘme tableau.
Exécutez maintenant le
test de fil stack.spec.js . Nous
exécutons Jest dans un fichier spécifique car nous ne voulons pas que d'autres tests soient corrompus. Voici le résultat:
La pile n'est pas un constructeur . Bien sûr. Nous n'avons toujours pas créé notre pile et n'avons pas fait de constructeur.
Dans
stack.js créez votre classe, constructeur et exportez la classe:
class Stack { constructor() { } } module.exports = Stack
Relancez le test:

Comme nous n'avons pas défini les éléments dans le constructeur,
Jest s'attendait à ce que les éléments du tableau soient égaux à [], mais ils n'étaient pas définis. Ensuite, vous devez initialiser les éléments:
constructor() { this.items = [] }
Si vous exĂ©cutez Ă nouveau le test, vous obtiendrez la mĂȘme erreur pour la
capacité , vous devrez donc également définir la capacité:
constructor(capacity) { this.items = [] this.capacity = capacity }
Exécutez le test:

Oui!
Test réussi . Voici ce qu'est TDD. J'espÚre que maintenant les tests auront plus de sens pour vous! Continuez?
isEmpty
Pour tester
isEmpty , nous allons initialiser une pile vide, tester si isEmpty renvoie true, ajouter un élément et le vérifier à nouveau.
it('Should have an isEmpty function that returns true if the stack is empty and false otherwise', () => { let stack = new Stack(3) expect(stack.isEmpty()).toBe(true) stack.items.push(2) expect(stack.isEmpty()).toBe(false) })
Lorsque vous exécutez le test, vous devriez obtenir l'erreur suivante:
TypeError: stack.isEmpty is not a function
Pour résoudre ce problÚme, nous devons créer
isEmpty dans la classe
Stack :
isEmpty () { }
Si vous exécutez le test, vous devriez obtenir une autre erreur:
Expected: true Received: undefined
Rien n'est ajoutĂ© Ă
isEmpty .
La pile est vide si elle ne contient aucun élément:
isEmpty () { return this.items.length === 0 }
isFull
C'est la mĂȘme chose que
isEmpty , car cet exercice teste cette fonction Ă l'aide de TDD. Vous trouverez la solution Ă la toute fin de l'article.
Poussez
Ici, nous devons tester
trois choses:
- Un nouvel Ă©lĂ©ment doit ĂȘtre ajoutĂ© en haut de la pile.
- push renvoie «Full» si la pile est pleine;
- Un Ă©lĂ©ment rĂ©cemment ajoutĂ© doit ĂȘtre retournĂ©.
Créez un autre bloc à l'aide de la
description de
push . Nous avons mis ce bloc à l'intérieur du bloc principal.
describe('Stack.push', () => { })
Ajouter un élément
Créez une nouvelle pile et ajoutez un élément. Le dernier élément du tableau d'
Ă©lĂ©ments doit ĂȘtre l'Ă©lĂ©ment que vous venez d'ajouter.
describe('Stack.push', () => { it('Should add a new element on top of the stack', () => { let stack = new Stack(3) stack.push(2) expect(stack.items[stack.items.length - 1]).toBe(2) }) })
Si vous exécutez le test, vous verrez que le
push n'est pas défini et c'est normal.
push aura besoin d'un paramĂštre pour ajouter quelque chose Ă la pile:
push (element) { this.items.push(element) }
Le test a réussi à nouveau. Avez-vous remarqué quelque chose? Nous conservons une copie de cette ligne:
let stack = new Stack(3)
C'est trÚs ennuyeux. Heureusement, nous avons une méthode
beforeEach qui vous permet de faire une certaine personnalisation avant chaque exécution de test. Pourquoi ne pas en profiter?
let stack beforeEach(() => { stack = new Stack(3) })
Important:
la pile doit ĂȘtre dĂ©clarĂ©e avant
beforeEach . En fait, si vous la définissez dans la méthode
beforeEach , la variable de pile ne sera pas définie dans tous les tests car elle ne se trouve pas dans la zone correcte.
Plus important qu'important: nous devons également créer une méthode
afterEach . L'instance de pile sera désormais utilisée pour tous les tests. Cela peut entraßner des difficultés. Immédiatement aprÚs
avant Ajoutez
chacun cette méthode:
afterEach(() => { stack.items = [] })
Vous pouvez maintenant supprimer l'initialisation de la pile dans tous les tests. Voici le fichier de test complet:
const Stack = require('./stack') describe('Stack', () => { let stack beforeEach(() => { stack = new Stack(3) }) afterEach(() => { stack.items = [] }) it('Should constructs the stack with a given capacity', () => { expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) }) it('Should have an isEmpty function that returns true if the stack is empty and false otherwise', () => { stack.items.push(2) expect(stack.isEmpty()).toBe(false) }) describe('Stack.push', () => { it('Should add a new element on top of the stack', () => { stack.push(2) expect(stack.items[stack.items.length - 1]).toBe(2) }) }) })
Test de la valeur de retour
Il y a un test:
it('Should return the new element pushed at the top of the stack', () => { let elementPushed = stack.push(2) expect(elementPushed).toBe(2) })
Lorsque vous exécutez le test, vous obtiendrez:
Expected: 2 Received: undefined
Rien ne revient à l'intérieur de
push ! Nous devons résoudre ce problÚme:
push (element) { this.items.push(element) return element }
Retour plein si la pile est pleine
Dans ce test, nous devons d'abord remplir la pile, ajouter un élément, nous assurer que rien de superflu n'a été ajouté à la pile et que la valeur de retour est
Full .
it('Should return full if one tries to push at the top of the stack while it is full', () => { stack.items = [1, 2, 3] let element = stack.push(4) expect(stack.items[stack.items.length - 1]).toBe(3) expect(element).toBe('Full') })
Vous verrez cette erreur lorsque vous exécuterez le test:
Expected: 3 Received: 4
Donc, l'article est ajouté. Voilà ce que nous voulions. Vous devez d'abord vérifier si la pile est pleine avant d'ajouter quelque chose:
push (element) { if (this.isFull()) { return 'Full' } this.items.push(element) return element }
Test réussi.Exercice: pop and peekIl est temps de pratiquer. Testez et implémentez
pop and
peek .
Astuces:
- pop est trĂšs similaire Ă pousser
- coup d'oeil est aussi comme push
- Jusqu'à présent, nous n'avons pas refactorisé le code, car cela n'était pas nécessaire. Il peut y avoir un moyen dans ces fonctions de refactoriser votre code aprÚs avoir écrit les tests et les avoir passés. Ne vous inquiétez pas, changer le code, des tests sont nécessaires pour cela.
Ne regardez pas la solution ci-dessous sans essayer de le faire vous-mĂȘme. La seule façon de progresser est d'essayer, d'expĂ©rimenter et de pratiquer.
Solution
Eh bien, comment est l'exercice? L'avez-vous fait? Sinon, ne vous découragez pas. Les tests demandent du temps et des efforts.
class Stack { constructor (capacity) { this.items = [] this.capacity = capacity } isEmpty () { return this.items.length === 0 } isFull () { return this.items.length === this.capacity } push (element) { if (this.isFull()) { return 'Full' } this.items.push(element) return element } pop () { return this.isEmpty() ? 'Empty' : this.items.pop() } peek () { return this.isEmpty() ? 'Empty' : this.items[this.items.length - 1] } } module.exports = Stack const Stack = require('./stack') describe('Stack', () => { let stack beforeEach(() => { stack = new Stack(3) }) afterEach(() => { stack.items = [] }) it('Should construct the stack with a given capacity', () => { expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) }) it('Should have an isEmpty function that returns true if the stack is empty and false otherwise', () => { expect(stack.isEmpty()).toBe(true) stack.items.push(2) expect(stack.isEmpty()).toBe(false) }) it('Should have an isFull function that returns true if the stack is full and false otherwise', () => { expect(stack.isFull()).toBe(false) stack.items = [4, 5, 6] expect(stack.isFull()).toBe(true) }) describe('Push', () => { it('Should add a new element on top of the stack', () => { stack.push(2) expect(stack.items[stack.items.length - 1]).toBe(2) }) it('Should return the new element pushed at the top of the stack', () => { let elementPushed = stack.push(2) expect(elementPushed).toBe(2) }) it('Should return full if one tries to push at the top of the stack while it is full', () => { stack.items = [1, 2, 3] let element = stack.push(4) expect(stack.items[stack.items.length - 1]).toBe(3) expect(element).toBe('Full') }) }) describe('Pop', () => { it('Should removes the last element at the top of a stack', () => { stack.items = [1, 2, 3] stack.pop() expect(stack.items).toEqual([1, 2]) }) it('Should returns the element that have been just removed', () => { stack.items = [1, 2, 3] let element = stack.pop() expect(element).toBe(3) }) it('Should return Empty if one tries to pop an empty stack', () => {
Si vous regardez les fichiers, vous verrez que j'ai utilisé la triple condition en pop and peek. C'est ce que j'ai refactorisé. L'ancienne implémentation ressemblait à ceci:
if (this.isEmpty()) { return 'Empty' } return this.items.pop()
Le développement à travers les tests nous permet de refactoriser le code une fois les tests écrits, j'ai trouvé une implémentation plus courte, sans se soucier du comportement de mes tests.
Relancez le test:

Les tests améliorent la qualité du code. J'espÚre que vous comprenez maintenant tous les avantages des tests et que vous utiliserez TDD plus souvent.
Nous vous avons convaincu que les tests sont cruciaux et améliorent la qualité du code? Lisez plutÎt la partie 2 de l'article sur le développement par le biais de tests. Et qui n'a pas lu le premier,
suivez le lien .