Im vorherigen Artikel haben wir theoretische Aspekte untersucht. Es ist Zeit zu üben.

Lassen Sie uns eine einfache Implementierung des Stacks in JavaScript mithilfe der Entwicklung durch Testen durchführen.
Stack - eine Datenstruktur, die nach dem Prinzip von LIFO organisiert ist: Last In, First Out. Es gibt drei Hauptoperationen auf dem Stapel:
push : Element hinzufügen
Pop : Löscht ein Element
Peek :
Kopfelement hinzufügen
Erstellen Sie eine Klasse und nennen Sie sie Stapel. Angenommen, der Stapel hat eine feste Kapazität, um die Aufgabe zu verkomplizieren. Hier sind die Eigenschaften und Implementierungsfunktionen unseres Stacks:
Gegenstände : Gegenstände auf dem Stapel. Wir werden ein Array verwenden, um den Stack zu implementieren.
Kapazität : Stapelkapazität.
isEmpty () :
Gibt true zurück, wenn der Stapel leer ist, andernfalls false.
isFull () :
Gibt true zurück, wenn der Stapel die maximale Kapazität erreicht, d.
h. wenn Sie kein weiteres Element hinzufügen können. Andernfalls wird false zurückgegeben.
push (Element) : Fügt ein Element hinzu. Gibt Full zurück, wenn der Stack voll ist.
pop : Entfernt das Element. Gibt leer zurück, wenn der Stapel leer ist.
peek () : Fügt ein
Kopfelement hinzu.
Wir werden zwei Dateien
stack.js und
stack.spec.js erstellen . Ich habe die Erweiterung
.spec.js verwendet, weil ich daran
gewöhnt bin, aber Sie können .test.js verwenden oder ihm einen anderen Namen geben und ihn in
__tests__ verschieben .
Da wir die Entwicklung durch Testen üben, werden wir einen fehlgeschlagenen Test schreiben.
Überprüfen Sie zunächst den Konstruktor. Um die Datei zu testen, müssen Sie die Stack-Datei importieren:
const Stack = require('./stack')
Für diejenigen, die daran interessiert sind, warum ich hier keinen
Import verwendet habe, unterstützt die neueste stabile Version von Node.js diese Funktion heute nicht. Ich könnte Babel hinzufügen, aber ich möchte dich nicht überladen.
Wenn Sie eine Klasse oder Funktion testen, führen Sie einen Test aus und beschreiben Sie, welche Datei oder Klasse Sie testen. Hier geht es um den Stapel:
describe('Stack', () => { })
Dann müssen wir überprüfen, ob beim Initialisieren des Stapels ein leeres Array erstellt wird, und wir stellen die richtige Kapazität ein. Also schreiben wir den folgenden Test:
it('Should constructs the stack with a given capacity', () => { let stack = new Stack(3) expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) })
Beachten Sie, dass wir
toEqual verwenden und
toBe nicht für
stack.items verwenden, da diese nicht auf dasselbe Array verweisen.
Führen Sie nun den
Garntest stack.spec.js aus . Wir führen
Jest in einer bestimmten Datei aus, weil wir nicht möchten, dass andere Tests beschädigt werden. Hier ist das Ergebnis:
Stack ist kein Konstruktor . Natürlich. Wir haben unseren Stack noch nicht erstellt und keinen Konstruktor erstellt.
Erstellen Sie in
stack.js Ihre Klasse, Ihren Konstruktor und exportieren Sie die Klasse:
class Stack { constructor() { } } module.exports = Stack
Führen Sie den Test erneut aus:

Da wir die Elemente im Konstruktor nicht festgelegt haben, erwartete
Jest, dass die Elemente im Array gleich [] sind, aber sie wurden nicht definiert. Dann sollten Sie die Elemente initialisieren:
constructor() { this.items = [] }
Wenn Sie den Test erneut ausführen, wird der gleiche Fehler für die
Kapazität angezeigt. Daher müssen Sie auch die Kapazität festlegen:
constructor(capacity) { this.items = [] this.capacity = capacity }
Führen Sie den Test aus:

Ja!
Test bestanden . Hier ist was TDD ist. Ich hoffe, jetzt macht das Testen für Sie mehr Sinn! Weiter?
isEmpty
Um
isEmpty zu testen,
initialisieren wir einen leeren Stapel, testen, ob isEmpty true zurückgibt, fügen ein Element hinzu und überprüfen es erneut.
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) })
Wenn Sie den Test ausführen, sollte der folgende Fehler angezeigt werden:
TypeError: stack.isEmpty is not a function
Um dieses Problem zu lösen, müssen wir
isEmpty in der
Stack- Klasse erstellen:
isEmpty () { }
Wenn Sie den Test ausführen, sollte ein weiterer Fehler angezeigt werden:
Expected: true Received: undefined
IsEmpty wird nichts hinzugefügt.
Der Stapel ist leer, wenn keine Elemente enthalten sind:
isEmpty () { return this.items.length === 0 }
isFull
Dies ist dasselbe wie
isEmpty , da diese Übung diese Funktion mit TDD testet. Die Lösung finden Sie ganz am Ende des Artikels.
Drücken Sie
Hier müssen wir
drei Dinge testen:
- Ein neues Element muss oben im Stapel hinzugefügt werden.
- push gibt "Full" zurück, wenn der Stack voll ist;
- Ein kürzlich hinzugefügter Artikel muss zurückgegeben werden.
Erstellen Sie einen weiteren Block mit
description for
push . Wir setzen diesen Block in den Hauptblock.
describe('Stack.push', () => { })
Element hinzufügen
Erstellen Sie einen neuen Stapel und fügen Sie ein Element hinzu. Das letzte Element im Elementarray sollte das Element sein, das Sie gerade hinzugefügt haben.
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) }) })
Wenn Sie den Test ausführen, werden Sie feststellen, dass
Push nicht definiert ist und dies normal ist.
push benötigt einen Parameter, um dem Stapel etwas hinzuzufügen:
push (element) { this.items.push(element) }
Test erneut bestanden. Hast du etwas bemerkt? Wir behalten eine Kopie dieser Zeile:
let stack = new Stack(3)
Das ist sehr nervig. Glücklicherweise haben wir eine
beforeEach- Methode, mit der Sie vor jedem Testlauf einige Anpassungen
vornehmen können. Warum nicht davon profitieren?
let stack beforeEach(() => { stack = new Stack(3) })
Wichtig:
Der Stapel muss vor jedem deklariert
werden . Wenn Sie es in der
beforeEach- Methode definieren, wird die
Stapelvariable nicht in allen Tests definiert, da sie sich nicht im richtigen Bereich befindet.
Wichtiger
als wichtig: Wir müssen auch eine
afterEach- Methode erstellen. Die Stapelinstanz wird jetzt für alle Tests verwendet. Dies kann einige Schwierigkeiten verursachen.
Fügen Sie unmittelbar nach
jedem diese Methode hinzu:
afterEach(() => { stack.items = [] })
Jetzt können Sie die Stapelinitialisierung in allen Tests entfernen. Hier ist die komplette Testdatei:
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) }) }) })
Rückgabewertprüfung
Es gibt einen Test:
it('Should return the new element pushed at the top of the stack', () => { let elementPushed = stack.push(2) expect(elementPushed).toBe(2) })
Wenn Sie den Test ausführen, erhalten Sie:
Expected: 2 Received: undefined
Nichts kehrt in
Push zurück ! Wir müssen das beheben:
push (element) { this.items.push(element) return element }
Geben Sie voll zurück, wenn der Stapel voll ist
In diesem Test müssen wir zuerst den Stapel füllen, ein Element hinzufügen, sicherstellen, dass dem Stapel nichts Zusätzliches hinzugefügt wurde und der Rückgabewert
Voll ist .
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') })
Dieser Fehler wird angezeigt, wenn Sie den Test ausführen:
Expected: 3 Received: 4
Also wird der Artikel hinzugefügt. Das wollten wir. Zuerst müssen Sie überprüfen, ob der Stapel voll ist, bevor Sie etwas hinzufügen:
push (element) { if (this.isFull()) { return 'Full' } this.items.push(element) return element }
Test bestanden.Übung: Pop und PeekEs ist Zeit zu üben. Testen und implementieren Sie
Pop and
Peek .
Tipps:
- Pop ist Push sehr ähnlich
- Peek ist auch wie Push
- Bisher haben wir den Code nicht überarbeitet, da dies nicht erforderlich war. In diesen Funktionen kann es eine Möglichkeit geben, Ihren Code zu überarbeiten, nachdem Sie die Tests geschrieben und bestanden haben. Machen Sie sich keine Sorgen, ändern Sie den Code, dafür sind Tests erforderlich.
Schauen Sie sich die unten stehende Lösung nicht an, ohne es selbst zu versuchen. Der einzige Weg zum Fortschritt besteht darin, zu versuchen, zu experimentieren und zu üben.
Lösung
Wie ist die Übung? Hast du es getan Wenn nicht, lassen Sie sich nicht entmutigen. Das Testen kostet Zeit und Mühe.
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', () => {
Wenn Sie sich die Dateien ansehen, werden Sie sehen, dass ich die dreifache Bedingung in Pop and Peek verwendet habe. Das habe ich überarbeitet. Die alte Implementierung sah folgendermaßen aus:
if (this.isEmpty()) { return 'Empty' } return this.items.pop()
Die Entwicklung durch Testen ermöglicht es uns, den Code nach dem Schreiben der Tests umzugestalten. Ich fand eine kürzere Implementierung, ohne mir Gedanken über das Verhalten meiner Tests zu machen.
Führen Sie den Test erneut aus:

Tests verbessern die Codequalität. Ich hoffe, Sie verstehen jetzt die vollen Vorteile des Testens und werden TDD häufiger verwenden.
Haben wir Sie davon überzeugt, dass das Testen entscheidend ist und die Qualität des Codes verbessert? Lesen Sie vielmehr Teil 2 des Artikels über Entwicklung durch Testen. Und wer das erste nicht gelesen hat,
folgt dem Link .