Pada artikel sebelumnya, kami memeriksa aspek teoritis. Sudah waktunya untuk mulai berlatih.

Mari kita buat implementasi sederhana stack di JavaScript menggunakan pengembangan melalui pengujian.
Stack - struktur data yang disusun berdasarkan prinsip LIFO: Last In, First Out. Ada tiga operasi utama pada stack:
push : tambah item
pop : hapus satu item
mengintip : tambahkan elemen kepala
Buat kelas dan beri nama Stack. Untuk menyulitkan tugas, anggap tumpukan memiliki kapasitas tetap. Berikut adalah properti dan fungsi implementasi dari stack kami:
item : item di tumpukan. Kami akan menggunakan array untuk mengimplementasikan stack.
kapasitas :
kapasitas tumpukan.
isEmpty () : mengembalikan true jika stack kosong, jika tidak palsu.
isFull () : mengembalikan true jika tumpukan mencapai kapasitas maksimum, yaitu ketika Anda tidak dapat menambahkan elemen lain. Jika tidak, mengembalikan false.
push (elemen) : menambahkan elemen. Pengembalian Penuh jika tumpukan penuh.
pop : menghapus item. Mengembalikan Kosong jika tumpukan kosong.
peek () : menambahkan elemen kepala.
Kita akan membuat dua file
stack.js dan
stack.spec.js . Saya menggunakan ekstensi
.spec.js karena saya sudah terbiasa, tetapi Anda dapat menggunakan .test.js atau memberinya nama yang berbeda dan memindahkannya ke
__tests__ .
Karena kami berlatih pengembangan melalui pengujian, kami akan menulis tes yang gagal.
Pertama, periksa konstruktor. Untuk menguji file, Anda perlu mengimpor file stack:
const Stack = require('./stack')
Bagi mereka yang tertarik mengapa saya tidak menggunakan
impor di sini, versi stabil terbaru Node.js tidak mendukung fitur ini hari ini. Saya bisa menambahkan Babel, tetapi saya tidak ingin membebani Anda.
Saat Anda menguji kelas atau fungsi, jalankan tes dan jelaskan file atau kelas mana yang Anda uji. Ini tentang tumpukan:
describe('Stack', () => { })
Kemudian kita perlu memeriksa bahwa ketika stack diinisialisasi, array kosong dibuat, dan kami mengatur kapasitas yang benar. Jadi, kami menulis tes berikut:
it('Should constructs the stack with a given capacity', () => { let stack = new Stack(3) expect(stack.items).toEqual([]) expect(stack.capacity).toBe(3) })
Perhatikan bahwa kita menggunakan
toEqual dan tidak menggunakan
toBe untuk
stack.items karena mereka tidak mereferensikan array yang sama.
Sekarang jalankan
uji benang stack.spec.js . Kami menjalankan
Jest dalam file tertentu karena kami tidak ingin tes lain rusak. Inilah hasilnya:
Stack bukan konstruktor . Tentu saja Kami masih belum membuat tumpukan dan belum membuat konstruktor.
Di
stack.js buat kelas Anda, konstruktor, dan ekspor kelas:
class Stack { constructor() { } } module.exports = Stack
Jalankan tes lagi:

Karena kami tidak menetapkan elemen dalam konstruktor,
Jest berharap elemen dalam array sama dengan [], tetapi mereka tidak didefinisikan. Maka Anda harus menginisialisasi elemen:
constructor() { this.items = [] }
Jika Anda menjalankan tes lagi, Anda akan mendapatkan kesalahan yang sama untuk
kapasitas , jadi Anda juga perlu mengatur kapasitas:
constructor(capacity) { this.items = [] this.capacity = capacity }
Jalankan tes:

Ya!
Tes berlalu . Inilah TDD. Saya harap sekarang pengujian akan lebih masuk akal untuk Anda! Lanjutkan?
isEmpty
Untuk menguji
isEmpty , kita akan menginisialisasi tumpukan kosong, uji apakah isEmpty mengembalikan true, tambahkan elemen dan periksa lagi.
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) })
Saat Anda menjalankan tes, Anda harus mendapatkan kesalahan berikut:
TypeError: stack.isEmpty is not a function
Untuk mengatasi masalah ini, kita perlu membuat
isEmpty di dalam kelas
Stack :
isEmpty () { }
Jika Anda menjalankan tes, Anda harus mendapatkan kesalahan lain:
Expected: true Received: undefined
Tidak ada yang ditambahkan ke
isEmpty .
Stack kosong jika tidak ada elemen di dalamnya:
isEmpty () { return this.items.length === 0 }
isFull
Ini sama dengan
isEmpty , karena latihan ini menguji fungsi ini menggunakan TDD. Anda akan menemukan solusinya di bagian paling akhir artikel.
Dorong
Di sini kita perlu menguji
tiga hal:
- Item baru harus ditambahkan ke bagian atas tumpukan.
- tekan kembali "Penuh" jika tumpukan penuh;
- Item yang baru saja ditambahkan harus dikembalikan.
Buat blok lain menggunakan
uraikan untuk
mendorong . Kami menempatkan blok ini di dalam yang utama.
describe('Stack.push', () => { })
Tambahkan item
Buat tumpukan baru dan tambahkan elemen. Item terakhir dalam array
item harus item yang baru saja Anda tambahkan.
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) }) })
Jika Anda menjalankan tes, Anda akan melihat bahwa
push tidak ditentukan dan ini normal.
push akan membutuhkan parameter untuk menambahkan sesuatu ke stack:
push (element) { this.items.push(element) }
Tes berlalu lagi. Apakah Anda memperhatikan sesuatu? Kami menyimpan salinan baris ini:
let stack = new Stack(3)
Ini sangat menyebalkan. Untungnya, kami memiliki metode
beforeEach yang memungkinkan Anda melakukan penyesuaian sebelum setiap pengujian dijalankan. Mengapa tidak memanfaatkan ini?
let stack beforeEach(() => { stack = new Stack(3) })
Penting:
tumpukan harus dideklarasikan sebelum sebelum
Setiap . Bahkan, jika Anda mendefinisikannya dalam metode
beforeEach , variabel stack tidak akan didefinisikan dalam semua tes karena tidak berada di area yang benar.
Lebih penting
daripada penting: kita juga harus membuat metode
afterEach . Contoh tumpukan sekarang akan digunakan untuk semua tes. Ini dapat menyebabkan beberapa kesulitan. Segera setelah
beforeEach tambahkan metode ini:
afterEach(() => { stack.items = [] })
Sekarang Anda dapat menghapus inisialisasi tumpukan di semua tes. Ini adalah file tes lengkap:
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) }) }) })
Pengujian nilai kembali
Ada tes:
it('Should return the new element pushed at the top of the stack', () => { let elementPushed = stack.push(2) expect(elementPushed).toBe(2) })
Saat Anda menjalankan tes, Anda akan mendapatkan:
Expected: 2 Received: undefined
Tidak ada yang mengembalikan
dorongan dalam! Kami harus memperbaiki ini:
push (element) { this.items.push(element) return element }
Kembali penuh jika tumpukan penuh
Dalam tes ini, pertama-tama kita perlu mengisi tumpukan, menambahkan elemen, memastikan bahwa tidak ada yang berlebihan telah ditambahkan ke tumpukan dan bahwa nilai kembalinya
Penuh .
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') })
Anda akan melihat kesalahan ini saat menjalankan tes:
Expected: 3 Received: 4
Jadi item ditambahkan. Inilah yang kami inginkan. Pertama, Anda perlu memeriksa apakah tumpukan sudah penuh sebelum menambahkan sesuatu:
push (element) { if (this.isFull()) { return 'Full' } this.items.push(element) return element }
Tes berlalu.Latihan: pop dan mengintipSaatnya berlatih. Uji dan laksanakan
pop dan
mengintip .
Kiat:
- pop sangat mirip dengan push
- Mengintip juga seperti mendorong
- Sejauh ini, kami belum refactored kode, karena ini tidak perlu. Mungkin ada cara dalam fungsi-fungsi ini untuk memperbaiki kode Anda setelah menulis tes dan meneruskannya. Jangan khawatir, mengubah kode, tes diperlukan untuk ini.
Jangan melihat solusi di bawah ini tanpa mencoba melakukannya sendiri. Satu-satunya cara untuk maju adalah dengan mencoba, bereksperimen, dan berlatih.
Solusi
Bagaimana latihannya? Apakah kamu melakukannya? Jika tidak, jangan berkecil hati. Pengujian membutuhkan waktu dan usaha.
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', () => {
Jika Anda melihat file, Anda akan melihat bahwa saya menggunakan kondisi triple di pop dan mengintip. Inilah yang saya refactored. Implementasi lama terlihat seperti ini:
if (this.isEmpty()) { return 'Empty' } return this.items.pop()
Pengembangan melalui pengujian memungkinkan kami untuk memperbaiki kode setelah tes ditulis, saya menemukan implementasi yang lebih singkat tanpa mengkhawatirkan perilaku pengujian saya.
Jalankan tes lagi:

Tes meningkatkan kualitas kode. Saya harap Anda sekarang memahami manfaat penuh pengujian dan akan menggunakan TDD lebih sering.
Apakah kami meyakinkan Anda bahwa pengujian sangat penting dan meningkatkan kualitas kode? Sebaliknya, baca bagian 2 artikel tentang pengembangan melalui pengujian. Dan siapa yang tidak membaca yang pertama,
ikuti tautannya .