Développement par le test: améliorer les compétences

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

image

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:

image

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:

image

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:

image

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 peek

Il 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', () => { // By default, the stack is empty expect(stack.pop()).toBe('Empty') }) }) describe('Peek', () => { it('Should returns the element at the top of the stack', () => { stack.items = [1, 2, 3] let element = stack.peek() expect(element).toBe(3) }) it('Should return Empty if one tries to peek an empty stack', () => { // By default, the stack is empty expect(stack.peek()).toBe('Empty') }) }) }) 

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:


image

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 .

Source: https://habr.com/ru/post/fr430876/


All Articles