{i>Tooltrade

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 sebagai assert(...) atau assert.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 metode assert.equal dan assert.strictEqual.
  • 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 helper deepEqual atau deepStrictEqual.

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:

Daftar file dalam
  termasuk {i>UserList.tsx<i} 
dan {i>UserList.test.tsx<i}.
File komponen dan file pengujian terkait.

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', () => {  });