Advertisement
  1. Code
  2. Coding Fundamentals
  3. Testing

Menguji JavaScript Anda dengan Jasmine

Scroll to top
Read Time: 13 min

() translation by (you can also view the original English article)

Kita semua tahu kita harus menguji kode Anda, tetapi kita tidak benar-benar melakukannya. Saya kira itu adil untuk mengatakan bahwa kebanyakan dari kita menundanya karena, sembilan dari sepuluh, itu berarti mempelajari konsep lain. Dalam tutorial ini, saya akan memperkenalkan Anda pada kerangka kecil yang hebat untuk menguji kode JavaScript Anda dengan mudah.


Langkah 0: Memahami BDD

Hari ini, kita akan belajar tentang framework pengujian Jasmine BDD. Tetapi kita berhenti di sini untuk memutar terlebih dahulu, untuk berbicara secara singkat, tentang BDD dan TDD. Jika Anda tidak terbiasa dengan akronim ini, mereka mendukung Behaviour-Driven Development dan Test-Driven Development. Saya di tengah-tengah belajar tentang apa yang masing-masing dalam praktik dan bagaimana mereka berbeda, tetapi berikut adalah beberapa perbedaan mendasar:

BDD dan TDD… singkatan dari Behavior-Driven Development dan Test-Driven Development.

TDD dalam bentuknya yang paling sederhana adalah hanya ini:

  1. Tulis tes Anda
  2. Lihat mereka gagal
  3. Buat mereka lewat
  4. Refactor
  5. Ulangi

Ini cukup mudah untuk dipahami, kan?

BDD sedikit lebih kompleks: seperti yang saya mengerti sekarang, saya tidak berpikir bahwa Anda atau saya sebagai pengembang tunggal benar-benar dapat mempraktikkannya sepenuhnya; ini lebih merupakan hal tim. Berikut adalah beberapa praktik BDD:

  • Menetapkan tujuan dari pemangku kepentingan yang berbeda diperlukan agar visi dapat diimplementasikan
  • Melibatkan pemangku kepentingan dalam proses implementasi melalui pengembangan software luar-dalam
  • Menggunakan contoh untuk menggambarkan perilaku aplikasi, atau unit kode
  • Mengotomatiskan contoh-contoh itu untuk memberikan umpan balik cepat dan pengujian regresi

Untuk mempelajari lebih lanjut, Anda dapat membaca Artikel Wikipedia yang luas (dari mana poin-poin itu diambil).

Semua ini untuk mengatakan itu, sementara Jasmine menyebut dirinya sebagai framework BDD, kita akan menggunakannya dengan cara yang lebih bergaya TDD. Itu tidak berarti kita salah menggunakannya. Setelah selesai, Anda dapat menguji JavaScript dengan mudah... dan saya berharap Anda melakukannya!


Langkah 1: Mempelajari Sintaksnya

Jasmine membutuhkan banyak isyarat dari Rspec.

Jika Anda sama sekali tidak asing dengan Rspec, framework BDD de facto, Anda akan melihat bahwa Jasmine membutuhkan banyak isyarat dari Rspec. Tes Jasmine pada dasarnya adalah dua bagian: describe blok dan it blok. Mari kita lihat bagaimana ini bekerja.

Kita akan melihat beberapa tes yang lebih dekat dengan kehidupan nyata dalam beberapa, tetapi untuk saat ini, kita akan membuatnya sederhana:

1
describe('JavaScript addition operator', function () {
2
    it('adds two numbers together', function () {
3
        expect(1 + 2).toEqual(3);
4
    });
5
});

Baik fungsi describe dan it fungsinya mengambil dua parameter: string teks dan fungsi. Sebagian besar frameworks tes mencoba membaca sebanyak mungkin bahasa Inggris, dan Anda dapat melihatnya dengan Jasmine. Pertama, perhatikan bahwa string yang diteruskan untuk describe dan string it yang dilewatinya membentuk kalimat (semacam): "Operator penambahan JavaScript menambahkan dua angka bersama-sama." Lalu, kita lanjutkan untuk menunjukkan caranya.

Di dalamnya blok it, Anda dapat menulis semua kode pengaturan yang Anda butuhkan untuk pengujian Anda. Kita tidak membutuhkan apa pun untuk contoh sederhana ini. Setelah Anda siap untuk menulis kode pengujian yang sebenarnya, Anda akan mulai dengan fungsi expect, meneruskannya apa pun yang Anda uji. Perhatikan bagaimana ini membentuk kalimat juga: kita “mengharapkan 1 + 2 sama dengan 3.”

Tapi saya maju dari diri kita sendiri. Seperti saya katakan, nilai berapa pun yang Anda lulus ke expect akan diuji. Metode yang Anda panggil, dari nilai expect yang dikembalikan, akan ditentukan oleh tes mana yang dijalankan. Kelompok metode ini disebut 'pencocokan', dan kita akan melihat beberapa dari mereka hari ini. Dalam hal ini, kita menggunakan pencocokan toEqual, yang memeriksa untuk melihat bahwa nilai yang diteruskan sesuai expect dan nilai yang diteruskan keEqual adalah nilai yang sama.

Saya pikir Anda siap untuk mengambil ini ke tingkat berikutnya, jadi mari kita buat proyek sederhana menggunakan Jasmine.


Langkah 2: Menyiapkan Proyek

Jasmine dapat digunakan dengan sendirinya; atau Anda dapat mengintegrasikannya dengan proyek Rails. Kita akan melakukan yang pertama. Sementara Jasmine dapat berjalan di luar browser (pikirkan Node, di antara tempat-tempat lain), kita bisa mendapatkan templat kecil yang sangat bagus dengan unduhan.

Jadi, buka halaman unduh mandiri dan dapatkan versi terbaru. Anda harus mendapatkan sesuatu seperti ini:

Jasmine DownloadJasmine DownloadJasmine Download

Anda akan menemukan file framework Jasmine yang sebenarnya di folder lib. Jika Anda lebih suka untuk menyusun proyek Anda secara berbeda, silakan lakukan; tapi kita akan menyimpan ini untuk saat ini.

Sebenarnya ada beberapa kode sampel yang ditransfer dalam templat proyek ini. JavaScript "aktual" (kode yang ingin diuji) dapat ditemukan di subdirektori src; kita akan menempatkan di sana segera. Kode pengujian—spesifikasi—masuk ke folder spec. Jangan khawatir tentang file SpecHelper.js dulu; kita akan kembali ke sana.

File SpecRunner.html itulah yang menjalankan tes di browser. Buka (dan centang kotak "lulus" di sudut kanan atas), dan Anda akan melihat sesuatu seperti ini:

Sample TestsSample TestsSample Tests

Ini menunjukkan kepada kita bahwa semua tes untuk proyek sampel lulus. Setelah Anda menyelesaikan tutorial ini, saya sarankan Anda membuka file spec/PlayerSpec.js dan membaca dengan teliti kode itu. Tapi sekarang, mari kita coba ujian menulis ini.

  • Buat convert.js di folder src.
  • Buat convertSpec.js di folder spec,
  • Salin file SpecRunner.html dan beri nama SpecRunner.original.html.
  • Hapus tautan ke file proyek sampel di SpecRunner.html dan tambahkan baris ini:

    1
    <script src="src/convert.js"><>/script>
    
    
    2
    <script src="spec/convertSpec.js"></script>
    

Sekarang kita siap membuat perpustakaan mini yang akan mengkonversi antar unit pengukuran. Kita akan mulai dengan menulis tes untuk perpustakaan mini.


Langkah 3: Menulis Tes

Jadi, mari tulis tes kita, ya?

1
describe( "Convert library", function () {
2
    describe( "distance converter", function () {
3
4
        });
5
6
        describe( "volume converter", function () {
7
8
    });
9
});

Kita mulai dengan ini; kita sedang menguji perpustakaan Convert. Anda akan melihat bahwa kita sedang describe pernyataan di sini. Ini sangat legal. Ini sebenarnya cara yang bagus untuk menguji potongan fungsionalitas terpisah dari basis kode yang sama. Alih-alih dua panggilan describe yang terpisah untuk Convert jarak konversi perpustakaan dan konversi volume, kita dapat memiliki serangkaian tes yang lebih deskriptif seperti ini.

Sekarang, ke tes yang sebenarnya. Saya akan mengulangi panggilan yang describe di dalam sini untuk kenyamanan Anda.

1
describe( "distance converter", function () {
2
    it("converts inches to centimeters", function () {
3
        expect(Convert(12, "in").to("cm")).toEqual(30.48);
4
    });
5
6
    it("converts centimeters to yards", function () {
7
        expect(Convert(2000, "cm").to("yards")).toEqual(21.87);
8
    });
9
});

Berikut ini adalah pengujian kita untuk konversi jarak. Penting untuk memperhatikan sesuatu di sini: kita belum menulis setitik kode untuk perpustakaan Convert, jadi dalam tes ini melakukan lebih dari sekadar memeriksa untuk melihat apakah itu berfungsi: kita benar-benar memutuskan bagaimana itu akan digunakan (dan karenanya diimplementasikan). Inilah cara kita memutuskan untuk melakukan konversi:

1
Convert(<number>, <from unit string>).to(<to unit string>);

Ya, saya mengambil petunjuk dari cara Jasmine menerapkan tesnya, tapi saya pikir ini format yang bagus. Jadi, dalam dua tes ini, saya telah membuat konversi sendiri (ok, dengan kalkulator) untuk melihat seperti apa hasil panggilan. Kita menggunakan pencocokan toEqual untuk melihat apakah pengujian lulus.

Inilah tes volume:

1
describe( "volume converter", function () {
2
    it("converts litres to gallons", function () {
3
        expect(Convert(3, "litres").to("gallons")).toEqual(0.79);
4
    });
5
6
    it("converts gallons to cups", function () {
7
        expect(Convert(2, "gallons").to("cups")).toEqual(32);
8
    });
9
});

Dan saya akan menambahkan dua tes lagi di panggilan describe tingkat atas:

1
it("throws an error when passed an unknown from-unit", function () {
2
    var testFn = function () {
3
        Convert(1, "dollar").to("yens");
4
    }
5
    expect(testFn).toThrow(new Error("unrecognized from-unit"));
6
});
7
8
it("throws an error when passed an unknown to-unit", function () {
9
    var testFn = function () {
10
        Convert(1, "cm").to("furlongs");
11
    }
12
    expect(testFn).toThrow(new Error("unrecognized to-unit"));
13
});

Ini memeriksa kesalahan yang harus dilemparkan ketika unit tidak dikenal dilewatkan ke salah satu fungsi Convert atau ke metode to. Anda akan melihat bahwa saya membungkus konversi yang sebenarnya dalam suatu fungsi dan meneruskannya ke fungsi expect. Itu karena kita tidak dapat memanggil fungsi sebagai parameter expect; kita perlu memberikan fungsi dan membiarkannya memanggil fungsi itu sendiri. Karena kita perlu mengirimkan parameter to berfungsi, kita dapat melakukannya dengan cara ini.

Hal lain yang perlu diperhatikan adalah saya memperkenalkan pencocokan baru: toThrow, yang mengambil objek kesalahan. Kita akan segera melihat beberapa pertandingan lagi.

Sekarang, jika Anda membuka SpecRunner.html di browser, Anda akan mendapatkan ini:

Convert failed testsConvert failed testsConvert failed tests

Hebat! Tes kita gagal. Sekarang, mari kita buka file convert.js dan lakukan beberapa pekerjaan:

1
function Convert(number, fromUnit) {
2
    var conversions = {
3
            distance : {
4
                meters : 1,
5
                cm     : 0.01,
6
                feet   : 0.3048,
7
                inches : 0.0254,
8
                yards  : 0.9144
9
            },
10
            volume : {
11
                liters : 1,
12
                gallons: 3.785411784,
13
                cups   : 0.236588236 
14
            }
15
        },
16
        betweenUnit = false,
17
        type, unit;
18
19
    for (type in conversions) {
20
        if (conversions(type)) {
21
            if ( (unit = conversions[type][fromUnit]) ) {
22
                betweenUnit = number * unit * 1000;
23
            }
24
        }
25
    }
26
27
    return {
28
        to : function (toUnit) {
29
            if (betweenUnit) {
30
                for (type in conversions) {
31
                    if (conversions.hasOwnProperty(type)) {
32
                        if ( (unit = conversions[type][toUnit]) ) {
33
                            return fix(betweenUnit / (unit * 1000));
34
                        }
35
                    }
36
                }
37
                throw new Error("unrecognized to-unit");
38
            } else {
39
                throw new Error("unrecognized from-unit");
40
            }  
41
42
            function fix (num) {
43
                return parseFloat( num.toFixed(2) );
44
            }
45
        }
46
    };
47
}

Kita tidak akan membahas ini, karena kita mempelajari Jasmine di sini. Tapi inilah poin utamanya:

  • Kita melakukan konversi dengan menyimpan konversi di suatu objek; nomor konversi diklasifikasikan berdasarkan jenis (jarak, volume, tambahkan milik Anda). Untuk setiap bidang pengukuran, kita memiliki nilai dasar (meter atau liter, di sini) yang dikonversi menjadi segalanya. Jadi, ketika Anda melihat yards: 0.9144, Anda tahu bahwa itu adalah berapa banyak yard yang ada dalam satu meter. Kemudian, untuk mengubah yard menjadi, katakanlah, sentimeter, kita mengalikan yards dengan parameter pertama (untuk mendapatkan jumlah meter) dan kemudian membagi produk dengan cm, jumlah meter dalam sentimeter. Dengan cara ini, kita tidak perlu menyimpan tingkat konversi untuk setiap pasangan nilai. Ini juga memudahkan untuk menambahkan nilai baru nanti.
  • Dalam kasus ini, kita mengharapkan unit yang diteruskan akan sama dengan kunci yang digunakan di "tabel" konversi. Jika ini adalah perpustakaan asli, kita ingin mendukung banyak format—seperti ‘in’, ‘inch’, dan ‘inci’—dan oleh karena itu kita ingin menambahkan beberapa logika untuk mencocokkan fromUnit ke tombol kanan.
  • Di akhir fungsi Convert, kita menyimpan nilai perantara di betweenUnit, yang diinisialisasi ke false. Dengan begitu, jika kita tidak memiliki fromUnit, betweenUnit akan false masuk ke metode to, dan dengan demikian kesalahan dengan dibuang.
  • Jika kita tidak memiliki toUnit, kesalahan yang berbeda akan terjadi. Kalau tidak, kita akan membagi seperlunya dan mengembalikan nilai yang dikonversi.

Sekarang, kembali ke SpecRunner.html dan muat ulang halaman. Anda sekarang akan melihat ini (setelah memeriksa "Show passed"):

Ini dia! Tes kita lulus. Jika kita mengembangkan proyek nyata di sini, kita akan menulis tes untuk sejumlah fungsionalitas, membuat mereka lulus, menulis tes untuk cek lain, membuatnya lulus, dll. Tetapi karena ini adalah contoh sederhana, kita baru saja melakukan semuanya sekaligus.

Dan sekarang setelah Anda melihat contoh sederhana menggunakan Jasmine, mari kita lihat beberapa fitur yang ditawarkannya.


Langkah 4: Mempelajari Pencocokan

Sejauh ini, kita telah menggunakan dua pencocokan: toEqual dan toThrow. Tentu saja ada banyak lainnya. Berikut beberapa yang mungkin berguna bagi Anda; Anda dapat melihat seluruh daftar di wiki.

toBeDefined / toBeUndefined

Jika Anda hanya ingin memastikan variabel atau properti didefinisikan, ada yang cocok untuk itu. Ada juga yang mengonfirmasi bahwa suatu variabel atau properti tidak undefined.

1
it("is defined", function () {
2
    var name = "Andrew";
3
    expect(name).toBeDefined();
4
})
5
6
it("is not defined", function () {
7
    var name;
8
    expect(name).toBeUndefined();
9
});

toBeTruthy / toBeFalsy

Jika sesuatu itu benar atau salah, pencocokan ini akan melakukannya.

1
it("is true", function () {
2
    expect(Lib.isAWeekDay()).toBeTruthy();
3
});
4
it("is false", function () {
5
    expect(Lib.finishedQuiz).toBeFalsy();
6
});

toBeLessThan / toBeGreaterThan

Untuk semua yang Anda nomor orang. Anda tahu cara kerjanya:

1
it("is less than 10", function () {
2
    expect(5).toBeLessThan(10);
3
});
4
it("is greater than 10", function () {
5
    expect(20).toBeGreaterThan(10);
6
});

toMatch

Punya beberapa teks keluaran yang harus cocok dengan ekspresi reguler? Matcher toMatch sudah siap dan mau.

1
it("outputs the right text", function () {
2
    expect(cart.total()).toMatch(/\$\d*.\d\d/);
3
});

toContain

Yang ini sangat berguna. Ia memeriksa untuk melihat apakah array atau string berisi item atau substring.

1
it("should contain oranges", function () {
2
    expect(["apples", "oranges", "pears"]).toContain("oranges");
3
});

Ada beberapa pencocokan lain yang juga bisa Anda temukan di wiki. Tetapi bagaimana jika Anda menginginkan korek api yang tidak ada? Sungguh, Anda harus bisa melakukan apa saja dengan beberapa kode pengaturan dan pencocokan yang disediakan Jasmine, tetapi kadang-kadang lebih baik untuk mengabstraksi beberapa logika untuk melakukan tes yang lebih mudah dibaca. Secara kebetulan (yah, sebenarnya tidak), Jasmine memungkinkan untuk membuat korek api kita sendiri. Tetapi untuk melakukan ini, kita harus belajar sedikit sesuatu yang lain terlebih dahulu.

Langkah 5: Menutupi Sebelum dan Setelah

Seringkali — saat menguji basis kode — Anda ingin melakukan beberapa baris kode penyiapan untuk setiap pengujian dalam satu seri. Akan menyakitkan dan secara verbal harus menyalinnya untuk setiap panggilan it, sehingga Jasmine memiliki fitur kecil yang berguna yang memungkinkan kita untuk menetapkan kode untuk dijalankan sebelum atau setelah setiap pengujian. Mari kita lihat cara kerjanya:

1
describe("MyObject", function () {
2
    var obj = new MyObject();
3
4
    beforeEach(function () {
5
        obj.setState("clean");
6
    });
7
8
    it("changes state", function () {
9
        obj.setState("dirty");
10
        expect(obj.getState()).toEqual("dirty");
11
    })
12
    it("adds states", function () {
13
        obj.addState("packaged");
14
        expect(obj.getState()).toEqual(["clean", "packaged"]);
15
    })
16
});

Dalam contoh yang dibuat-buat ini, Anda dapat melihat bagaimana, sebelum setiap tes dijalankan, status obj diatur ke "bersih". Jika kita tidak melakukan ini, perubahan yang dibuat ke objek dalam tes sebelumnya tetap menjadi tes berikutnya secara default. Tentu saja, kita juga bisa melakukan sesuatu yang mirip dengan fungsi AfterEach:

1
describe("MyObject", function () {
2
    var obj = new MyObject("clean"); // sets initial state
3
4
    afterEach(function () {
5
        obj.setState("clean");
6
    });
7
8
    it("changes state", function () {
9
        obj.setState("dirty");
10
        expect(obj.getState()).toEqual("dirty");
11
    })
12
    it("adds states", function () {
13
        obj.addState("packaged");
14
        expect(obj.getState()).toEqual(["clean", "packaged"]);
15
    })
16
});

Di sini, kita menyiapkan objek untuk memulai, dan kemudian memperbaikinya setelah setiap pengujian. Jika Anda ingin fungsi MyObject sehingga Anda dapat mencoba kode ini, Anda bisa mendapatkannya di sini di inti GitHub.

Langkah 6: Menulis Pencocokan Kustom

Seperti yang dikatakan sebelumnya, pencocokan pelanggan mungkin akan sangat membantu. Jadi mari kita tulis satu. Kita dapat menambahkan korek api di panggilan BeforeEach atau panggilan it (well, saya kira Anda bisa melakukannya dalam panggilan AfterEach, tapi itu tidak masuk akal). Inilah cara Anda memulai:

1
beforeEach(function () {
2
    this.addMatchers({
3
4
    });
5
});

Cukup sederhana, kan? Kita menyebutnya this.addMatchers, meneruskannya sebagai parameter objek. Setiap kunci dalam objek ini akan menjadi nama pencocokan, dan fungsi terkait (nilainya) adalah cara dijalankannya. Katakanlah kita ingin membuat pencocokan yang dengan cek untuk melihat apakah satu nomor di antara dua lainnya. Inilah yang Anda tulis:

1
beforeEach(function () {
2
    this.addMatchers({
3
        toBeBetween: function (rangeFloor, rangeCeiling) {
4
            if (rangeFloor > rangeCeiling) {
5
                var temp = rangeFloor;
6
                rangeFloor = rangeCeiling;
7
                rangeCeiling = temp;
8
            }
9
            return this.actual > rangeFloor && this.actual < rangeCeiling;
10
        }
11
    });
12
});

Kita hanya mengambil dua parameter, pastikan yang pertama lebih kecil dari yang kedua, dan mengembalikan pernyataan boolean yang bernilai true jika kondisi terpenuhi. Hal penting yang perlu diperhatikan di sini adalah bagaimana kita mendapatkan nilai yang diteruskan ke fungsi expect: this.actual.

1
it("is between 5 and 30", function () {
2
    expect(10).toBeBetween(5, 30);
3
});
4
5
it("is between 30 and 500", function () {
6
    expect(100).toBeBetween(500, 30);
7
});

Inilah yang dilakukan oleh file SpecHelper.js; ini memiliki panggilan beforeEach yang menambahkan pencocokan tobePlaying(). Periksa!


Kesimpulan: Bersenang-senanglah!

Ada banyak lagi yang dapat Anda lakukan dengan Jasmine: pencocokan terkait fungsi, mata-mata, spesifikasi asinkron, dan banyak lagi. Saya sarankan Anda menjelajahi wiki jika Anda tertarik. Ada juga beberapa perpustakaan yang menyertainya yang membuat pengujian di DOM lebih mudah: Jasmine-jQuery, dan Jasmine-fixture (yang tergantung pada Jasmine-jQuery).

Jadi, jika Anda belum menguji JavaScript Anda sejauh ini, sekarang adalah waktu yang tepat untuk memulai. Seperti yang telah kita lihat, sintaksis Jasmine yang cepat dan sederhana membuat pengujian menjadi sangat sederhana. Tidak ada alasan bagi Anda untuk tidak melakukannya, sekarang, kan?

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.