Pengujian otomatis pada dasarnya hanyalah kode yang akan menampilkan atau menyebabkan kesalahan jika ada yang salah. Sebagian besar library atau framework pengujian menyediakan berbagai primitif yang membuat pengujian lebih mudah ditulis.
Seperti disebutkan di bagian sebelumnya, primitif ini hampir selalu mencakup untuk menentukan pengujian independen (disebut sebagai kasus pengujian) dan untuk pernyataan. Pernyataan adalah cara menggabungkan pemeriksaan hasil dan menampilkan {i>error <i}jika ada sesuatu yang salah, dan dapat dianggap sebagai dasar dari semua primitif pengujian.
Halaman ini membahas pendekatan umum untuk primitif tersebut. Framework yang Anda pilih mungkin memiliki sesuatu seperti ini, tapi ini bukan referensi yang tepat.
Contoh:
import { fibonacci, catalan } from '../src/math.js';
import { assert, test, suite } from 'a-made-up-testing-library';
suite('math tests', () => {
test('fibonacci function', () => {
// check expected fibonacci numbers against our known actual values
// with an explanation if the values don't match
assert.equal(fibonacci(0), 0, 'Invalid 0th fibonacci result');
assert.equal(fibonacci(13), 233, 'Invalid 13th fibonacci result');
});
test('relationship between sequences', () => {
// catalan numbers are greater than fibonacci numbers (but not equal)
assert.isAbove(catalan(4), fibonacci(4));
});
test('bugfix: check bug #4141', () => {
assert.isFinite(fibonacci(0)); // fibonacci(0) was returning NaN
})
});
Contoh ini membuat grup pengujian (terkadang disebut suite) yang disebut "matematika pengujian", dan menentukan tiga kasus pengujian independen yang masing-masing menjalankan beberapa pernyataan. Kasus pengujian ini biasanya dapat ditangani atau dijalankan secara individual, misalnya, flag filter pada runner pengujian Anda.
Penunjang pernyataan sebagai primitif
Sebagian besar framework pengujian, termasuk Vitest, menyertakan kumpulan pernyataan
pembantu pada objek assert
yang memungkinkan Anda dengan cepat memeriksa nilai yang ditampilkan atau
status lain terhadap beberapa ekspektasi. Ekspektasi tersebut sering kali "baik"
masing-masing. Pada contoh sebelumnya, kita tahu
angka Fibonacci ke-13 seharusnya
233, jadi kita dapat mengonfirmasinya secara langsung menggunakan assert.equal
.
Anda mungkin juga memiliki ekspektasi bahwa suatu nilai memiliki bentuk tertentu, atau lebih besar dari nilai lain, atau memiliki beberapa properti lain. Kursus ini tidak akan mencakup berbagai kemungkinan helper pernyataan, tetapi menguji framework selalu menyediakan setidaknya pemeriksaan dasar berikut:
'truthy' {i>check<i}, yang sering kali digambarkan sebagai 'ok' memeriksa, memeriksa apakah suatu kondisi sudah benar, cocok dengan cara Anda menulis
if
yang memeriksa apakah sesuatu berhasil atau benar. Info ini cenderung disediakan sebagaiassert(...)
atauassert.ok(...)
, dan mengambil satu nilai ditambah komentar opsional.Pemeriksaan kesetaraan, seperti dalam contoh ujian matematika, di mana Anda mengharapkan nilai yang ditampilkan atau keadaan objek untuk menyamakan nilai baik yang diketahui. Pertanyaan ini untuk persamaan primitif (seperti untuk angka dan string) atau persamaan referensial (keduanya adalah objek yang sama). Di balik layar, semua ini hanyalah 'truthy' centang dengan perbandingan
==
atau===
.- JavaScript membedakan antara persamaan longgar (
==
) dan ketat (===
). Sebagian besar library pengujian menyediakan metodeassert.equal
danassert.strictEqual
.
- JavaScript membedakan antara persamaan longgar (
Pemeriksaan kesetaraan mendalam, yang memperluas pemeriksaan kesetaraan untuk mencakup isi objek, larik, dan tipe data lainnya yang lebih kompleks, serta logika internal untuk melintasi objek untuk membandingkannya. Ini penting karena JavaScript tidak memiliki cara bawaan untuk membandingkan isi dua objek atau array. Misalnya,
[1,2,3] == [1,2,3]
selalu salah (false). Uji coba framework sering kali menyertakan helperdeepEqual
ataudeepStrictEqual
.
Encoder pernyataan yang membandingkan dua nilai (bukan hanya pemeriksaan 'truthy') biasanya membutuhkan dua atau tiga argumen:
- Nilai sebenarnya, seperti yang dihasilkan dari kode yang diuji atau mendeskripsikan status untuk divalidasi.
- Nilai yang diharapkan, biasanya {i>hard-code<i} (misalnya, angka literal atau {i>string<i}).
- Komentar opsional yang menjelaskan apa yang diharapkan atau apa yang mungkin gagal, yang akan disertakan jika baris ini gagal.
Menggabungkan pernyataan untuk menyusun berbagai macam pernyataan pemeriksaan, karena jarang ada orang yang dapat mengonfirmasi status layanan sistem itu sendiri. Contoh:
test('JWT parse', () => {
const json = decodeJwt('eyJieSI6InNhbXRob3Ii…');
assert.ok(json.payload.admin, 'user should be admin');
assert.deepEqual(json.payload.groups, ['role:Admin', 'role:Submitter']);
assert.equal(json.header.alg, 'RS265')
assert.isAbove(json.payload.exp, +new Date(), 'expiry must be in future')
});
Vitest menggunakan library pernyataan Cha secara internal untuk menyediakan helper penegasannya, dan itu bisa berguna untuk mencari referensinya untuk melihat pernyataan dan helper yang mungkin sesuai dengan kode Anda.
Pernyataan lancar dan BDD
Beberapa developer lebih memilih gaya pernyataan yang bisa dipanggil berdasarkan perilaku
pengembangan (BDD), atau
Gaya Fluent
pernyataan. Hal ini juga disebut sebagai "ekspektasi" pembantu, karena titik masuk ke
memeriksa ekspektasi adalah metode bernama expect()
.
Diperkirakan helper berperilaku dengan cara yang sama seperti pernyataan yang ditulis sebagai metode sederhana
panggilan seperti assert.ok
atau assert.strictDeepEquals
, namun beberapa developer menganggapnya lebih mudah dibaca.
Pernyataan BDD mungkin terbaca seperti berikut:
// A failure here would generate "Expect result to be an array that does include 42"
const result = await possibleMeaningsOfLife();
expect(result).to.be.an('array').that.does.include(42);
// or a simpler form
expect(result).toBe('array').toContainEqual(42);
// the same in assert might be
assert.typeOf(result, 'array', 'Expected the result to be an array');
assert.include(result, 42, 'Expected the result to include 42');
Gaya pernyataan ini berfungsi karena teknik
yang disebut perantaian metode,
tempat objek yang ditampilkan oleh expect
dapat terus dirantai bersama dengan
panggilan metode lebih lanjut. Beberapa bagian panggilan, termasuk to.be
dan that.does
pada contoh sebelumnya, tidak memiliki fungsi dan hanya disertakan untuk melakukan panggilan
lebih mudah dibaca dan berpotensi menghasilkan
komentar otomatis jika pengujian
gagal. (Secara khusus, expect
biasanya tidak mendukung komentar opsional, karena
perantaian harus menggambarkan
kegagalan yang terjadi dengan jelas.)
Banyak framework pengujian mendukung pernyataan Fluent/BDD dan pernyataan reguler. Semangat, misalnya, mengekspor kedua Chai dan memiliki pendekatan yang sedikit lebih ringkas terhadap BDD. Jest di sisi lain, hanya menyertakan ekspektasi secara default.
Mengelompokkan pengujian di berbagai file
Saat menulis pengujian, kita cenderung menyediakan pengelompokan implisit—bukan daripada semua pengujian berada dalam satu file, biasanya pengujian ditulis di beberapa . Faktanya, {i>test runner<i} biasanya hanya mengetahui bahwa file itu untuk pengujian karena filter atau ekspresi reguler yang telah ditentukan—misalnya, menampilkan semua dalam project Anda yang diakhiri dengan ekstensi seperti ".test.jsx" atau ".spec.ts" (".test" dan ".spec" ditambah sejumlah ekstensi yang valid).
Pengujian komponen cenderung ditempatkan dalam file {i>peer-<i} ke komponen yang sedang diuji, seperti pada struktur direktori berikut:
Demikian pula, pengujian unit cenderung ditempatkan berdekatan dengan kode yang sedang diuji. Masing-masing pengujian menyeluruh mungkin berada dalam filenya sendiri, dan pengujian integrasi bahkan dapat ditempatkan dalam folder uniknya sendiri. Struktur-struktur ini dapat membantu ketika kasus pengujian yang rumit menjadi memerlukan file dukungan non-pengujian mereka sendiri, {i>support library<i} yang dibutuhkan hanya untuk pengujian.
Mengelompokkan pengujian dalam file
Seperti yang digunakan dalam contoh sebelumnya, melakukan pengujian di dalam panggilan
suite()
yang mengelompokkan pengujian yang Anda siapkan dengan test()
. Suite biasanya tidak
menguji dirinya sendiri, tetapi membantu menyediakan struktur dengan mengelompokkan
atau sasaran dengan memanggil metode yang diteruskan. Untuk test()
, metode yang diteruskan menjelaskan
tindakan pengujian itu sendiri.
Seperti pernyataan, terdapat padanan yang cukup standar di Fluent/BDD untuk pengelompokan pengujian. Beberapa contoh umum dibandingkan dalam kode berikut:
// traditional/TDD
suite('math tests', () => {
test('handle zero values', () => {
assert.equal(fibonacci(0), 0);
});
});
// Fluent/BDD
describe('math tests', () => {
it('should handle zero values', () => {
expect(fibonacci(0)).toBe(0);
});
})
Di sebagian besar framework, suite
dan describe
berperilaku serupa, seperti halnya test
dan
it
, dibandingkan dengan perbedaan yang lebih besar antara penggunaan expect
dan assert
untuk menulis pernyataan.
Alat lainnya memiliki pendekatan yang sedikit berbeda untuk mengatur suite dan pengujian. Sebagai
misalnya, runner pengujian bawaan Node.js mendukung panggilan bertingkat ke test()
untuk
secara implisit membuat
hierarki pengujian. Namun, Vitest hanya mengizinkan
bertingkat menggunakan suite()
dan tidak akan menjalankan test()
yang ditentukan di dalam test()
lain.
Sama seperti pernyataan, ingatlah bahwa kombinasi pengelompokan yang tepat yang disediakan tech stack Anda bukanlah hal yang penting. Kursus ini akan membahas secara abstrak, tetapi Anda harus mencari tahu bagaimana itu berlaku untuk pilihan alat.
Metode siklus proses
Salah satu alasan untuk mengelompokkan pengujian Anda, bahkan secara implisit di tingkat teratas dalam adalah menyediakan metode penyiapan dan pemutusan yang berjalan untuk setiap pengujian, atau sekali untuk sekelompok tes. Sebagian besar framework menyediakan empat metode:
Untuk setiap 'test()' atau 'it()', | Sekali untuk suite | |
---|---|---|
Sebelum pengujian dijalankan | `beforeSetiap()` | `beforeAll()` |
Setelah pengujian dijalankan | `afterSetiap()` | 'afterAll()' |
Misalnya, Anda mungkin ingin mengisi otomatis {i>database<i} pengguna virtual sebelum uji, lalu menghapusnya setelahnya:
suite('user test', () => {
beforeEach(() => {
insertFakeUser('bob@example.com', 'hunter2');
});
afterEach(() => {
clearAllUsers();
});
test('bob can login', async () => { … });
test('alice can message bob', async () => { … });
});
Hal ini dapat bermanfaat untuk menyederhanakan pengujian Anda. Anda dapat membagikan pengaturan umum dan kode pemutusan kode, bukan menduplikasinya dalam setiap pengujian. Selain itu, jika kode pengaturan dan pemutusan itu sendiri memunculkan kesalahan, yang dapat menunjukkan masalah yang tidak melibatkan pengujian itu sendiri yang gagal.
Saran umum
Berikut beberapa kiat yang perlu diingat ketika memikirkan tentang dasar-dasar ini.
Model primitif adalah panduan
Ingatlah bahwa alat dan primitif di sini, dan dalam beberapa halaman berikutnya, tidak sama persis dengan Vitest, Jest, atau Mocha, atau Web Test Runner, atau kerangka kerja spesifik. Meskipun kami menggunakan Vitest sebagai panduan umum, pastikan untuk mereka ke pilihan kerangka kerja.
Mengombinasikan pernyataan sesuai kebutuhan
Pengujian pada dasarnya adalah kode yang dapat menampilkan error. Setiap pelari akan memberikan
primitif, kemungkinan test()
, untuk mendeskripsikan kasus pengujian yang berbeda.
Namun, jika runner itu juga menyediakan assert()
, expect()
, dan helper pernyataan,
ingat bahwa bagian ini lebih tentang kenyamanan
dan Anda dapat melewatkannya jika
jika diperlukan. Anda dapat menjalankan kode apa pun yang mungkin menampilkan error, termasuk other
library pernyataan, atau pernyataan if
kuno yang baik.
Penyiapan IDE dapat sangat membantu
Memastikan bahwa IDE Anda, seperti VSCode, memiliki akses ke pelengkapan otomatis dan
dokumentasi terkait alat pengujian pilihan Anda, dapat meningkatkan produktivitas Anda. Sebagai
ada lebih dari 100 metode di assert
dalam pernyataan Chai
library, dan memiliki dokumentasi untuk
yang kanan muncul inline bisa jadi lebih mudah.
Hal ini bisa menjadi sangat penting untuk beberapa framework pengujian yang mengisi namespace global dengan metode pengujiannya. Ini adalah perbedaan yang kecil, tapi sering kali Anda dapat menggunakan library pengujian tanpa mengimpornya jika library otomatis ditambahkan ke namespace global:
// some.test.js
test('using test as a global', () => { … });
Sebaiknya impor helper meskipun didukung secara otomatis,
karena memberi IDE cara yang jelas untuk mencari metode ini. (Anda mungkin memiliki
mengalami masalah ini saat membangun React, karena beberapa codebase memiliki
React
global, tetapi beberapa tidak, dan mengharuskannya diimpor di semua file menggunakan
React.)
// some.test.js
import { test } from 'vitest';
test('using test as an import', () => { … });