Pengantar Code Quality
- Get link
- X
- Other Apps
Pengantar Code Quality
Selamat datang dalam materi terakhir kelas ini!
Tak terasa kita telah melewati banyak halang rintang untuk menuju developer JavaScript yang andal. Ini menjadi bekal utama untuk menguasai seluruh kekuatan dari JavaScript dan mulai membangun aplikasi yang hebat. Namun, secanggih apa pun itu, aplikasi dapat menimbulkan kesan buruk jika bug terdapat padanya. Kita tidak mau user merasa kecewa, bukan? Nah, mari kita pelajari materi code quality!
Secara kebahasaan, code quality adalah kualitas kode. Benar adanya bahwa kode yang bagus dan berkualitas adalah kode yang ditulis seminimal dan sesederhana mungkin. Namun, pelajaran kita akan berfokus pada pembangunan program yang memiliki karakteristik kuat dan aman dari bugs. Ada tiga topik untuk kita bahas guna meningkatkan kualitas kode.
- Pemanfaatan fitur type check untuk JavaScript guna meningkatkan kepercayaan diri developer.
- Menjaga konsistensi gaya penulisan kode sesuai dengan aturan-aturan yang ditetapkan.
- Menambahkan safety net, yaitu pengujian otomatis (automated test) sebagai salah satu praktik untuk mencegah error dan membantu terbebas dari bugs.
Cukup familier atau baru mendengar beberapa istilah di atas? Tidak masalah jika belum karena kita akan bahas semuanya dari nol. Berikut adalah beberapa objektif pembelajaran yang akan Anda capai pada materi ini.
- Menjabarkan tools yang dapat digunakan untuk mengecek tipe data ketika menulis kode JavaScript.
- Menguji sebuah fungsi sederhana dalam JavaScript dengan menggunakan teknik unit test.
- Memperbaiki kode dengan gaya yang tidak konsisten menjadi konsisten berdasarkan aturan yang didefinisikan.
Type System dalam JavaScript
Pada awal materi, kita mempelajari dua kubu bahasa pemrograman, yaitu bahasa yang menggunakan compiler dan interpreter. Namun, itu diidentifikasi dari cara atau waktu sebuah program dapat dieksekusi oleh mesin.
Ada indikator lain yang juga dapat membedakan beragam bahasa, yaitu type system. Ini terbagi menjadi dua, yaitu static dan dynamic. Dalam hal ini, JavaScript termasuk tipe dynamic.
Apa artinya itu? Ayo, kita bahas.
Statically-Typed vs. Dynamically-Typed Language
Setiap bahasa pemrograman memiliki sistem pemeriksaan tipe data. Ada yang sistemnya bertipe static dan dynamic. Hal yang membedakan adalah waktu pemeriksaannya.
- Static: tipe nilai diperiksa saat compile.
- Dynamic: tipe nilai diperiksa ketika eksekusi (runtime).
Dua perbedaan di atas mirip dengan perbedaan antara compiler dan interpreter, bukan? Namun, jangan sampai tertukar, ya, karena mereka berbeda secara konseptual.
Ciri utama lainnya yang menjadi pembeda adalah adanya penentuan tipe nilai saat deklarasi sebuah unit. Contohnya variabel. Bagi Anda yang familier dengan Java, C++, atau Golang, mereka adalah bahasa berkategori statically-typed. Bahasa-bahasa ini akan memberi batasan terhadap nilai yang dapat diemban oleh variabel karena pemeriksaan tipe nilai biasanya terjadi pada waktu compile.
Berikut adalah contoh-contoh penentuan tipe pada berbagai bahasa.
- public class Main {
- public static void main(String[] args) {
- int myNum = 15;
- System.out.println(myNum);
- }
- }
Lihat! Setiap variabel myNum dibuat dan diisyaratkan untuk menyimpan nilai integer (number untuk JavaScript). Compiler akan mengembalikan tipe error jika kita memberi nilai selain integer.
Penentuan tipe nilai di atas tidak kita temui pada JavaScript. Bahasa berkategori dynamically-typed akan mengetahui tipe datanya saat program berjalan. Masih ingat dengan let dan const, bukan? Apa pun teknik pembuatannya, kita bisa memberi nilai apa pun pada sebuah variabel tanpa perlu menetapkan tipe data secara eksplisit. Jika menggunakan let, nilai variabel memungkinkan untuk diganti saat runtime.
Perhatikan contoh kasus berikut.
let myNum = 0;myNum = 1;console.log(myNum) // 1myNum = true;console.log(myNum) // trueSemua berjalan lancar ketika program di atas dijalankan meskipun ada perubahan tipe nilai yang di-assign. Poinnya adalah JavaScript sangat fleksibel terhadap perubahan tipe data.
Lalu, apa maksud dari masalah ini?
The Problem
Kita mungkin punya pendapat bahwa bahasa yang mengadopsi dynamic type sangat menguntungkan dari segi produktivitas. Tidak bertele-tele saat menulis kode, proses debugging lebih mudah, dan kompilasi menjadi lebih cepat menjadi faktor-faktor kebahagiaan tersendiri dari seorang developer.
Namun, faktanya adalah bahasa bertipe static cenderung lebih safe daripada dynamic. Apa penyebabnya? Mari kita lihat contoh kasus berikut.
function add(numA, numB) { return numA + numB;}console.log(add(1, 1)); // 2console.log(add(3, 2)); // 5console.log(add('5', 4)); // ???Apa tebakan Anda dari program di atas? Ada kasus nilai string ditambahkan dengan number. Ini tidak akan menjadi masalah (biasanya type error) karena JavaScript memiliki fitur type coercion. Jika kita tidak berhati-hati dengan masalah ini, program akan berakhir dengan hasil yang aneh, yaitu “54” (string). Selain dynamic, JavaScript juga tergolong weakly-typed language.
Permasalahan di atas datang dari sisi interpreter milik JavaScript. Jika berbicara mengenai IDE (integrated development environment), permasalahan ini pun tidak dapat diatasi begitu saja. IDE tidak memiliki kemampuan pengecekan dan pemberian peringatan jika terdapat kekeliruan penggunaan tipe data. Padahal, tujuan utamanya adalah memberikan pengalaman pengembangan aplikasi yang lebih baik, bukan?
Pada materi-materi berikutnya, kita akan mempelajari berbagai cara mengantisipasi error karena masalah dynamic type dalam JavaScript. Kita akan membuat dokumentasi kode dan menambahkan fitur type check pada JavaScript.
Agar tidak penasaran lagi, langsung saja kita mulai!
JSDoc, Alat Dokumentasi Kode
Salah satu upaya untuk meningkatkan kehati-hatian kita terhadap dynamic type adalah menyediakan dokumentasi kode. Dokumentasi mirip dengan kita memberi sebuah instruksi atau deskripsi mengenai tata cara penggunaan kode. Kita akan menggunakan JSDoc sebagai alat dokumentasinya.
JSDoc adalah sebuah alat untuk menghasilkan dokumentasi kode JavaScript. Untuk memberikan deskripsi, kita akan memanfaatkan fitur komentar langsung pada sumber kodenya. Lebih tepatnya, komentar disajikan sebelum kode itu sendiri. Komentar ini sebut saja JSDoc comment.
JSDoc memiliki prosedur yang perlu kita ikuti sebelum memulai mendokumentasikan kode. Hal ini karena ada JSDoc parser yang akan menganalisis kode kita. Kerennya, JSDoc dapat menghasilkan dokumentasi dalam bentuk halaman web, lho. Nanti kita akan lihat.
Setiap JSDoc comment harus dimulai dengan /**. Komentar yang menggunakan /*, /***, atau lebih dari tiga bintang (*) tidak akan diproses oleh JSDoc. Mari kita lihat contoh berikut.
- /** Say hello to world */
- function greet() {
- console.log('Hello, world!');
- }
Dokumentasi di atas adalah contoh paling sederhana, yaitu hanya menambahkan deskripsi pada function. Bagaimana contoh yang lebih kompleks? JSDoc memiliki JSDoc tag yang dapat memberikan informasi lebih terkait function. Misalnya, sebuah function memiliki beberapa parameter dan return value. Kita bisa memberikan informasi dalam JSDoc comment tentang itu.
- /**
- * Get add operation of two numbers.
- *
- * @param {number} numA - The first numeric operand
- * @param {number} numB - The second numeric operand
- * @returns {number} Sum of numA and numB
- */
- function add(numA, numB) {
- return numA + numB;
- }
JSDoc tag diawali dengan simbol @ dan diikuti dengan nama tag-nya. Kita melihat ada dua tag pada contoh kasus di atas.
- @param: menyediakan nama, tipe data, dan deskripsi untuk function parameter.
- @returns: menginformasikan nilai yang akan function keluarkan.
Ada banyak sekali tag yang tersedia untuk menghasilkan dokumentasi terbaik. Tidak hanya untuk function, JSDoc juga mampu mendokumentasikan class, object, import-export, dan sebagainya. Bahkan, JSDoc membaginya menjadi dua, yaitu block dan inline. Anda dapat bereksplorasi JSDoc tag pada halaman dokumentasi ini.
Bikin Dokumentasi Versi Web
Sebagaimana sudah disebutkan sebelumnya, JSDoc dapat menghasilkan dokumentasi dalam bentuk halaman web. Hal yang perlu dicatat adalah kita akan membutuhkan binary JSDoc dan mengeksekusinya melalui terminal. Pemanfaatan fitur ini harus melibatkan package manager, seperti npm dan ini di luar topik pembahasan kita.
Namun, jangan khawatir. Rasa penasaran Anda dapat hilang dengan langsung melihat hasil webnya. Anda bisa membuka halamannya di sini. Namun, bila ingin bermain dan melihatnya langsung, Anda bisa akses interactive code berikut. Pada file explorer, Anda akan melihat direktori “out” yang menjadi lokasi hasil dokumentasi web-nya.
Mantap! Terlihat lebih terpandu dengan adanya dokumentasi, ya. Bagi yang sering berkolaborasi dalam JavaScript, tentu ini sangat membantu agar komunikasi antar developer menjadi lancar, tanpa hambatan suatu apa pun.
Flow, Type Check Library
Kita sudah melihat keuntungan menggunakan JSDoc. Sangat membantu untuk terhindar dari bugs, ya. Ada tools lain yang dapat mengantisipasi dynamic type dari JavaScript. Tools ini berbeda dengan JSDoc karena implementasinya bukan dalam bentuk komentar, melainkan langsung mengubah kode JavaScript kita. Mari kita ulas Flow bersama-sama.

Flow adalah sebuah library (terjemahan dalam bahasa Indonesia: pustaka) yang dapat menambahkan pemeriksaan type untuk kode JavaScript. Library ini seakan-akan dapat menambahkan type check layer (lapisan pemeriksaan tipe) sebelum dieksekusi mesin. Justru ini membuat kita semakin produktif serta menulis kode semakin cepat, cerdas, penuh percaya diri, dan mudah untuk diperluas. Begitu dokumentasi resmi Flow menyebutnya.
Lalu, bagaimana penggunaan Flow dalam kode JavaScript? Kita hanya perlu menambahkan sedikit upaya agar JavaScript mendukung static type.
Perhatikan kode berikut.
- const myName: string = 'Flow';
- function greet(name: string) {
- console.log(`Hello, ${name}. My name is ${myName}`);
- }
- greet(123);
Waw, kita bisa menentukan tipe nilai dalam variabel dan parameter function. Mirip sekali dengan beberapa bahasa pemrograman lain. Cukup tambahkan titik dua (:) dan diikuti dengan tipe nilainya (type). Dalam Flow, ini dinamakan static type annotations. Dengan demikian, kita dapat meminta Flow untuk membaca dan menganalisis kode di atas agar terbebas dari bugs.
Flow Type Checker
Ekspektasi kita adalah terjadi kesalahan karena function greet diberi nilai number, padahal yang diminta adalah string. Berikut adalah umpan balik dari hasil analisis Flow.
Catatan: |
Perhatikan! Flow mengembalikan pesan error pada kita bahwa ada ketidakcocokan tipe nilai. Anda tahu apa yang perlu dilakukan, bukan? Mari kita perbaiki pemanggilan greet dengan nilai yang sesuai.
greet(123);// --> hapus: number menyebabkan error- greet('JavaScript');
Perbaikan ini akan mengosongkan jumlah error yang ditemukan Flow. Lanjut, kita bisa menjalankan kodenya dalam JavaScript runtime.
Flow Compiler
Tidak perlu kaget jika kita menemukan error saat menjalankan kode di atas dengan binary node. Ini bukan kode JavaScript yang standar tentunya. Kita perlu menghilangkan static type annotations sebelum dapat dijalankan oleh node. Hadirlah library bernama flow-remove-types dari Flow.
Kira-kira hasil penghapusannya seperti berikut.
- const myName = 'Flow';
- function greet(name) {
- console.log(`Hello, ${name}. My name is ${myName}`);
- }
- greet('JavaScript');
Sip! Kini, kita bisa jalankan kodenya dan berikut output-nya.
- Hello, JavaScript. My name is Flow
Cara belajar yang terbaik adalah belajar dari pengalaman. Anda bisa memanfaatkan interactive code berikut agar bisa bermain implementasi Flow secara langsung.
Demikian materi kita mengenai Flow sebagai type checker kode JavaScript. Kita sudah belajar pengalaman yang berbeda untuk mengantisipasi terjadinya error dari permasalahan dynamic type. Bermula dari dokumentasi menggunakan komentar hingga terjun langsung pada kode utamanya menggunakan Flow.
Kita masih punya satu materi terakhir yang berkaitan dengan type checker. Ia bukanlah sebuah teknik ataupun library, tetapi sebuah bahasa terusan (superset) dari JavaScript dan sangat marak digunakan para developer JavaScript.
Penasaran? Let’s check it out!
TypeScript, Superset dari JavaScript
Materi terakhir dalam type system untuk JavaScript adalah TypeScript. Apakah Anda bisa menebak apa TypeScript itu? Dia adalah bahasa pemrograman di atas JavaScript! Mari bereksplorasi.
JavaScript pada awalnya dibangun demi fleksibilitas dan tingkat kemudahan yang tinggi. Oleh karena itu, JavaScript sangat cocok untuk dipelajari sebagai langkah awal para developer. Namun, ini menjadi tidak ideal jika kita membangun aplikasi berskala besar dengan ribuan hingga jutaan baris kode. Ini yang sudah kita pahami sedari awal karena tidak ada type checker dalam interpreter JavaScript. Bahkan, JavaScript dikategorikan sebagai weakly-typed language karena saking fleksibelnya.
Microsoft membangun sebuah bahasa bernama TypeScript untuk memadukan fleksibilitas JavaScript dengan bahasa yang stricter (lebih ketat/kaku). TypeScript memiliki sistem yang dapat menambahkan fitur type pada kode JavaScript dan nantinya akan dianalisis oleh compiler milik TypeScript. Yup, ini mirip dengan Flow.
Jangan khawatir atas kompatibilitas TypeScript terhadap JavaScript. Seluruh fitur JavaScript tentunya ada dalam TypeScript, yakni arrow function, class, dan fitur-fitur terkini lainnya. Salah satu hal yang kami sukai dari TypeScript adalah ia menjadi bahasa paling digemari di dunia berdasarkan Survey Stackoverflow 2023 dan dipakai industri besar, seperti Google dan Amazon.
Coding dengan TypeScript
Penulisan kode TypeScript membutuhkan .ts sebagai ekstensi berkasnya. Lalu, sintaks kodenya pun tidak berbeda dengan Flow. Berikut contohnya.
- const myName: string = 'TypeScript';
- function greet(name: string) {
- console.log(`Hello, ${name}. My name is ${myName}`);
- }
- greet('JavaScript');
Sangat mudah, ya. Ini memang tidak terlihat berbeda dengan Flow. Tentu ini akan menghemat energi kita jika ingin beralih dari Flow ke TypeScript.
Kode JavaScript yang dibangun dengan TypeScript juga perlu diproses agar menjadi kode yang standar. Namun, jika Flow membutuhkan langkah pemeriksaan dan penghapusan, TypeScript mempermudah kita dengan sekali langkah selesai melalui penggabungan dua langkah tersebut.
TypeScript tetaplah membutuhkan compiler untuk memproses kodenya, yaitu tsc. Ia akan memeriksa sekaligus menghasilkan berkas .js yang siap eksekusi.
Berikut adalah hasil compile dari tsc.
- const myName = 'TypeScript';
- function greet(name) {
- console.log(`Hello, ${name}. My name is ${myName}`);
- }
- greet('JavaScript');
Sip! Kini, kita bisa jalankan hasil kode di atas dan berikut output-nya.
- Hello, JavaScript. My name is TypeScript
Sekali lagi. Guru terbaik adalah pengalaman. Anda bisa memanfaatkan interactive code berikut agar bisa bermain dengan TypeScript secara langsung.
Bila ingin menelusuri lebih dalam, TypeScript memiliki dokumentasi resmi yang membahas tuntas seluruh fitur-fiturnya. Silakan akses halaman The TypeScript Handbook, ya.
TypeScript dengan Bun
Meskipun merupakan sebuah bahasa superset dari JavaScript, TypeScript tidak bisa dijalankan oleh Node.js (untuk versi terbaru Node.js sudah dapat menjalankan TypeScript). Oleh karena itu, pada bahasan sebelumnya, kita memerlukan tsc agar diubah menjadi kode JavaScript yang valid. Namun, hal ini berbeda dengan runtime Bun.
Bun mendukung eksekusi langsung berkas berekstensi .ts. Jadi, kita tidak perlu langkah tambahan saat membangun aplikasi dengan TypeScript.
Coba Anda jalankan kode berikut menggunakan Bun. Pastikan format berkasnya adalah typescript yang valid, ya.
- function add(numA: number, numB: number): number {
- return numA + numB;
- }
- const result = add(2, 4);
- console.log('Hasil:', result);
Tiga tools telah kita pelajari untuk memberikan pengalaman ngoding dengan static type pada JavaScript. Good job! Kini, Anda bisa memanfaatkan setiap tools pada setiap proyek yang membutuhkannya dan merasakan kebebasan dari bugs yang mungkin terjadi dalam masa mendatang.
Style Guide
Pembicaraan style tidak hanya berlaku pada saat kita berurusan dengan keindahan, seperti dalam lukisan, fashion, dan arsitektur bangunan. Bisa dibilang mereka adalah sebuah seni. Dalam konteks pemrograman, JavaScript juga memiliki style guide yang mengedepankan konsistensi penulisan kode. Mari kita bahas.
Konsistensi menjadi hal yang sangat penting dalam pengembangan aplikasi. Meskipun sebetulnya hal tersebut tidak berpengaruh terhadap validitas sintaks kode JavaScript. Namun, ini akan berpengaruh pada banyak hal jika tidak kita tanamkan ke setiap developer.
Anda sudah belajar banyak dengan fitur-fitur JavaScript, bukan? Pada satu tujuan, ada beberapa cara yang bisa diambil dan preferensi para developer dapat berbeda. Bahkan, ini terjadi pada hal kecil dalam kehidupan, layaknya metode penyantapan bubur ayam, ada yang suka diaduk ataupun tidak. Keterbukaan pendapat inilah yang menyebabkan inkonsistensi terjadi dalam kolaborasi proyek ataupun perusahaan. Kita membutuhkan sebuah konvensi atau panduan agar semua tertata rapi dan konsisten. Inilah tujuan dari style guide dalam JavaScript.
Mari kita ambil beberapa contoh.
- Penggunaan tanda titik-koma (semicolon).
- Gaya nama variabel.
- Penempatan komentar dalam kode.
- Penggunaan block-code (curly-bracket), seperti dalam percabangan.
- Banyak assignment secara sekaligus dan masih banyak lainnya lagi.
Agar terbayang, kami punya contoh kode program yang inkonsisten. Silakan perhatikan kode berikut.
- let no = 'Semicolon'
- const my_favorite_color = '#112C85';
- let myNumber = 3;
- console.log(myNumber);
- var random = 1; // declaring random to 1
- if (foo) foo++;
- let a = b = c = 5;
Bagaimana pengalaman membaca kode Anda dari contoh di atas? Kami yakin Anda merasa tidak nyaman dan mungkin jiwa kerapiannya bergejolak. Nah, di sinilah konvensi penulisan kode dibutuhkan. Ada dua cara yang bisa kami bagikan dan ulas untuk mengatasi masalah ini, yaitu manual dan otomatis. Metode otomatis akan kami bahas dalam materi berikutnya.
Metode manual didefinisikan sebagai penyediaan dokumentasi style guide secara tertulis dan mengikuti aturan tersebut. Misalnya perusahaan kita atau diri kita secara personal memiliki halaman dokumentasi style guide dalam bentuk apa pun. Kita tinggal memahami dan selalu berpatokan pada panduan tersebut agar konsistensi tetap terjaga.
Kami memiliki contoh dokumentasi style guide yang dapat Anda akses di sini. Misalnya, kita akan memperbaiki kode di atas agar mengikuti aturan di sana. Berikut hasilnya.
- const no = 'Semicolon';
- const myFavoriteColor = '#112C85';
- const myNumber = 3;
- console.log(myNumber);
- // declaring random to 1
- const random = 1;
- if (foo) {
- foo++;
- }
- let a = 5;
- let b = 5;
- let c = 5;
Kode yang tidak konsisten mungkin memiliki keuntungan tersendiri, seperti lebih cepat, fleksibel, dan sangat bebas. Namun, itu tidak sebanding dengan dampak negatif yang mungkin terjadi, terutama dalam kolaborasi.
- Kode sulit dibaca dan dipelajari oleh sesama kolega developer sehingga produktivitas menurun.
- Kolaborasi menjadi sulit terjalin dengan baik, apalagi saat mengalami penggabungan dua buah perubahan kode dari developer yang berbeda.
- Jika ada developer baru, proses onboarding pun akan membutuhkan waktu lebih untuk memahami kode.
Di luar poin-poin di atas, beberapa kemungkinan dampak negatif lainnya bisa saja terjadi. Alangkah lebih baiknya bila kita antisipasi dan peringatkan sedini mungkin. Nah, secara tidak langsung, kita sudah menjadi developer JavaScript yang selangkah lebih baik dari lainnya. Mungkin, cara ini perlu kesadaran diri yang tinggi dan ketelatenan kuat. Namun, bagi yang memiliki code reviewer, kode kita bisa dicek dan divalidasi lebih lanjut lagi.
Code Convention dengan Linter Library
Kita sudah belajar mengenai code style guide untuk meningkatkan kualitas penulisan kode JavaScript. Cara manual juga telah dipelajari sebagai langkah antisipasi awal terjadinya inkonsistensi penulisan kode. Nah, metode berikutnya ini lebih kami rekomendasikan karena dilakukan dengan bantuan library. Ini bisa kita anggap sebagai code reviewer dalam aspek style guide.
Ada beberapa library yang dapat menunjang kualitas kode kita, yaitu ESLint, StandardJS, dan JSHint. Para linter ini akan memberikan feedback setelah berhasil menganalisis kode Anda jika terdeteksi inkonsistensi pada penulisannya. Tentunya, feedback dihasilkan melalui Terminal/CMD. Kami akan memilih ESLint karena kepopulerannya. Sejak materi ini ditulis, ESLint telah mencapai angka 35,6 juta pengunduhan setiap minggunya. Keren!
ESLint tidak hanya luar biasa hebat dari kepopulerannya. Ia memiliki banyak fitur yang dapat memenuhi kebutuhan kita terkait style guide. ESLint dapat memperbaiki kode JavaScript secara otomatis melalui perintah terminal dan kode kita akan berubah menjadi sesuai dengan aturan yang diberlakukan. Powerful!
ESLint menyediakan konfigurasi yang berkaitan dengan aturan penulisan atau disebut rule. Ada tiga kategori yang tersedia pada setiap rule-nya berdasarkan tingkat keparahan.
- “off” atau 0: aturan tersebut tidak dipermasalahkan atau dimatikan.
- “warn” atau 1: aturan ditetapkan sebagai peringatan saja saat dilanggar.
- “error” atau 2: aturan wajib dipatuhi dan program dapat mengalami error.
Rule yang ditetapkan dengan nilai “error” dapat memaksa developer agar mematuhinya. Ini sangat bermanfaat karena para developer benar-benar wajib mengikuti aturan yang berlaku. Sebuah program pun tidak akan bisa diluncurkan ke publik jika rule tersebut sampai dicederai.
Lalu, bagaimana bentuk penulisan aturan di ESLint? Berikut contohnya.
- {
- rules: {
- "no-duplicate-imports": "off",
- "no-use-before-define": "error",
- "constructor-super": "error",
- "no-var": "warn",
- "no-unreachable": "warn",
- "no-extra-boolean-cast": "warn"
- }
- }
Setiap rule memiliki nama unik dan maksudnya masing-masing. Berikut penjelasannya.
Rule | Keterangan | Mode |
|---|---|---|
no-duplicate-imports | Semua module dapat diimpor dengan lebih dari satu perintah import. | 0 |
no-use-before-define | Penggunaan unit (variabel, function, dsb.) harus dilalui oleh proses deklarasi. | 2 |
constructor-super | constructor function dari class turunan (child class) harus selalu dipanggil jika function tersebut dideklarasi. | 2 |
no-var | Penggunaan keyword const dan let lebih disarankan daripada var. | 1 |
no-unreachable | Kode yang dipastikan tidak dapat dicapai oleh mesin akan memunculkan pesan peringatan. | 1 |
no-extra-boolean-cast | Tidak ada pengonversian tipe boolean yang mubazir. | 1 |
Secara lebih rinci, enam aturan di atas dapat Anda pelajari secara mandiri pada halaman Rules Reference. Anda dapat bereksplorasi juga pada halaman tersebut jika ingin menemukan aturan-aturan secara lengkap.
Mungkin ada yang penasaran pada bentuk pesan error yang dikembalikan oleh ESLint. Berikut contoh umpan baliknya.
Umpan balik di atas dihasilkan setelah menjalankan perintah berikut.
- npx eslint src/main.js
Menakjubkan sekali, bukan? Nah, jika keadaannya seperti di atas, kode kita belum tentu akan langsung diperbaiki, ya. Lagi pula, kita diperintahkan untuk menambahkan argument --fix ke dalam perintah eslint jika ingin langsung diperbaiki.
- npx eslint src/main.js --fix
Anda ingin melihat isi kode dari main.js? Bermainlah pada interactive code berikut sehingga Anda mendapatkan gambaran yang lebih jelas.
Demikian pelajaran kita mengenai style guide. Kami sangat merekomendasikan ESLint agar dapat menjadi asisten pribadi semasa pengembangan aplikasi JavaScript. Pun, kami bisa merasakan ketertarikan Anda terhadap ESLint.
Pengujian Program
Sebagai seorang pengembang aplikasi JavaScript, mungkin kita sudah familier dengan praktik pengujian. Bahkan, bisa saja kita melakukannya secara tak sadar. Ini menjadi tanggung jawab besar bagi developer agar sistem berjalan tanpa kurang suatu apa pun di tangan end-user. Tentu, hal ini tertanam dalam diri Anda, bukan?
Secara istilah, pengujian adalah proses memastikan keberhasilan suatu sistem untuk mencegah kegagalan saat beroperasi. Ada dua metode yang bisa kita lakukan. Jika tidak secara manual, kita akan lakukan pengujian aplikasi secara otomatis. Ya, bisa otomatis, Anda tidak salah dengar.
Pengujian manual tentu banyak pihak mampu melakukannya. Cukup jalankan program dan perhatikan output yang dikeluarkan. Perbaikan akan dilakukan jika terjadi error. Namun, cara ini sangatlah rentan dilakukan sebab adanya keluputan manusia dan hal yang paling terasa adalah tingkat efisiensi sangat rendah. Tenaga manusia yang akan lebih banyak dibutuhkan di sini selain komputer. Jika memanfaatkan metode otomatis, kita bisa mendelegasikan tugas kepada mesin dan biarlah ia yang bekerja. Tidak mengenal waktu, letih, dan keluputan adalah karakteristik dari mesin.
Manual Lebih Mudah? Fix The Error
Sebelum belajar membuat kode testing otomatis, mari kita belajar memperbaiki kesalahan program yang mengandung beberapa bug dituntun oleh pengujian manual. Sebelum melanjutkan pembahasan, pastikan Anda sudah paham makna dari bug, ya!
Nah, kali ini, mari kita ambil contoh kasus program kalkulator total harga belanjaan yang perlu dibayar. Programnya sederhana, kok.
// Fungsi untuk menghitung total harga belanjaanfunction calculateTotal(shoppingCart) { let total = 0; // Penghitungan tagihan terjadi di sini… for (let i = 0; i <= shoppingCart.length; i += 1) { total += shoppingCart[i].price; } return total;}// Contoh data belanjaanconst shoppingCart = [ { name: 'Apple', price: 300 }, { name: 'Banana', price: 120 }, { name: 'Orange', price: 130 },];// Memanggil fungsi dan mencetak hasilnyaPada kode di atas, ada satu buah fungsi yang akan menghitung total harga. Jika dijalankan, kira-kira apa output yang akan dikembalikan? Silakan jalankan program dalam interactive code di atas. Ternyata hasilnya error.
Padahal, kita yakin semuanya baik-baik saja. Kita tidak menyadari betul penyebab rapuhnya sebuah program. Hal ini menuntun kita sebagai developer agar selalu dan semestinya berpikir secara kritis. Ingat bahwa program di atas terjadi error (kesalahan pada kode), bukan bug (kesalahan logika), ya.
Letak error terjadi pada conditional loop dalam for statement. Alih-alih mendapatkan data belanjaan, kita malah berlebihan dalam mengakses index data. Ini ditandai dengan mengakses shoppingCart[3]. Padahal, maksimal angka index array adalah 2. Betul, kan? Seharusnya, kita gunakan simbol perbandingan “<”, bukan “<=”.
Sebetulnya ada cara yang lebih aman dibanding menggunakan for statement. Karena sudah belajar functional programming, kita bisa memanfaatkan reduce untuk melakukan iterasi data array. Bahkan, for statement perlu dihindari dalam functional programming, kan? Oleh karena itu, mari sesuaikan kode seperti berikut.
- function calculateTotal(shoppingCart) {
- const total = 0;
- return shoppingCart.reduce(
- (accumulator, cartItem) => accumulator + cartItem.price,
- total,
- );
- }
Silakan jalankan ulang hasil perbaikan program di atas dan tidak ada error yang ditemukan seharusnya. Nice!
Next problem. Tidak cukup sampai di sini. Kita perlu berikan variasi data yang lebih agar dapat menyaksikan ketahanan program. Bagaimana dengan data belanjaan berikut? Sudah cukup, ya.
- const shoppingCart = [
- { name: 'Apple', price: 300 },
- { name: 'Banana', price: 120 },
- { name: 'Orange', price: 130 },
- { name: 'Watermelon', price: '160' },
- { name: 'Pineapple', price: null },
- { name: 'Grape', price: null },
- ];
Ada beberapa tambahan variasi data yang kita miliki di atas, yaitu price dengan nilai string dan null. Menjadi pertanyaan besar bahwa mengapa kita perlukan data-data seperti ini?
Sebetulnya, kita tidak memiliki kendali terhadap nilai apa yang akan diberikan ke function. Jika data diambil dari sumber luar seperti API, kita belum tahu apa bentuknya. Tentunya, data-data seperti di atas tidak kita ekspektasikan dan sangat mungkin dialami oleh aplikasi. Aplikasi tetap berjalan sangat baik, tetapi beda cerita dengan adanya bug. Ini perlu segera diatasi.
Oleh karena itu, kita membutuhkan validasi sebelum proses kalkulasi dilanjutkan. Contohnya berikut.
- function calculateTotal(shoppingCart) {
- const total = 0;
- return shoppingCart.reduce((accumulator, cartItem) => {
- if (typeof cartItem.price === 'number') {
- return accumulator + cartItem.price;
- } else {
- console.error(`Tipe data cartItem.price tidak valid:`, cartItem);
- return accumulator;
- }
- }, total);
- }
| Catatan: Meskipun ada data yang keliru, jangan lupa berikan return statement juga pada block else. |
Perhatikan perubahan kode di atas! Bukan meneruskan ke proses kalkulasi, bila proses validasi tidak lolos, kita alihkan program ke block else dan tampilkan pesan error dalam console. Ini terjadi karena ada keanehan dalam data belanjaan.
Jika disimpulkan, berikut adalah hasil akhir dari program.
// Fungsi untuk menghitung total harga belanjaanfunction calculateTotal(shoppingCart) { const total = 0; return shoppingCart.reduce((accumulator, cartItem) => { if (typeof cartItem.price === 'number') { return accumulator + cartItem.price; } else { console.error(`Tipe data cartItem.price tidak valid:`, cartItem); return accumulator; } }, total);}// Contoh data belanjaan dengan beberapa kasus edgeconst shoppingCart = [ { name: 'Apple', price: 300 }, { name: 'Banana', price: 120 }, { name: 'Orange', price: 130 }, { name: 'Watermelon', price: '160' },Selamat! Silakan Anda saksikan hasil program yang terbebas dari bug, ya.
Kini kita menjadi paham bahwa selain memiliki error, program pun berpotensi memiliki bug. Meskipun penyelesaian bug dilakukan manual, kini kita sadar bahwa kehati-hatian dalam pengembangan program itu sangat penting.
Pengalaman kita dalam pengujian ini betul-betul dilakukan manual. Kita jalankan programnya dan perbaiki kesalahan saat ditemukan anomali. Ini tidak akan kita temukan jika sudah bermain dengan teknik otomatis.
Uji Kode dengan Automated Test
Kita telah mencapai milestone memperbaiki program yang berpotensi terjadi bug. Kita uji dengan berbagai macam sampel data untuk menyaksikan ketahanan program dan akhirnya masalah yang muncul bisa diatasi. Sekali lagi! Manual sebetulnya memiliki efisiensi yang rendah dan bukanlah satu-satunya cara pengujian. Apa solusinya? Pengujian otomatis jawabannya.
Pengujian sebetulnya terbagi menjadi tiga bagian, yaitu unit test, service/integration test, dan UI test. Pengujian otomatis yang kita pelajari dalam materi ini adalah unit testing. Unit test akan menguji bagian terkecil dalam program seperti function. Jika ingin mendalami tipe testing lainnya, silakan simak artikel Test Pyramid.
Untungnya, runtime JavaScript seperti Node.js dan Bun memiliki built-in module (module bawaan) untuk melakukan pengujian secara otomatis. Pengujian ini sepenuhnya didelegasikan kepada mesin dan menampilkan umpan balik kepada kita jika ada keanehan hasil. Umpan balik ini bisa terjadi karena error atau bug yang ditemukan ketika program sedang diuji.
Pengujian otomatis akan memberikan dua kemungkinan hasil, yaitu pass dan fail. Jika pengujian dinyatakan lolos, runtime akan menandainya sebagai pass (biasanya diwarnai hijau), sedangkan runtime memberi hasil fail (biasanya diwarnai merah) saat pernyataan gagal muncul.
Sudah cukup pengantar kita kepada pengujian otomatis. Kita akan berlatih implementasinya pada materi berikutnya. Nah, hal berikutnya yang penting untuk kita pahami adalah apa urgensi dari unit testing dalam pengembangan program?
- Mendeteksi kemungkinan error sedini mungkin. Sebisa mungkin, kita sebagai developer menemukan bug lebih dahulu sebelum end-user.
- Aplikasi masih berfungsi dengan baik meskipun terjadi perubahan kode. Biasanya, sebelum dirilis ke publik, aplikasi harus dalam kondisi semua pengujian lolos.
- Proses pengujian dilakukan secara cepat dan efisien walaupun saat berkolaborasi. Makin besar aplikasi maka makin banyak juga yang diuji. Jika dilakukan secara manual, semua akan terasa sulit dan lama.
Ingat! Testing otomatis dapat membuat kita terhindar dari kebiasaan pengujian manual: jalankan program → saksikan hasil → perbaiki kode jika hasil aneh. Oke, mari kita sama-sama langsung ke materi berikutnya untuk belajar implementasi testing dalam aplikasi JavaScript.
Testing dalam Node.js
Pengujian pada Node.js membutuhkan dua buah module, yaitu node:test dan node:assert. node:test berperan sebagai test runner yang menawarkan API untuk menuliskan skenario pengujian. Adapun node:assert berperan sebagai test assertion yang menyediakan objek untuk memvalidasi nilai antara actual (nilai sesungguhnya) dan expected (nilai yang diharapkan).
Susun Pengaman dengan Testing
Mari kita contohkan langsung dengan kasus sederhana. Kita memiliki satu function yang perlu diuji ketepatan dan kekuatannya. Function itu adalah add. Ia adalah salah satu fungsi kalkulator dan membutuhkan dua parameter yang akan diproses dengan operasi penjumlahan. Perhatikan kode berikut.
- fungsi ekspor tambah(numA, numB) {
- kembalikan numA + numB;
- }
Kami kira ini sudah cukup untuk bahan latihan kita. Sebelum menguji, ada baiknya kita tentukan dahulu skenario-skenario pengujian bersifat positif dan negatif. Berikut daftarnya.
- Function add dapat mengoperasikan penjumlahan aritmetika dengan baik.
- Function add membangkitkan error jika nilai argumen dari
numAtidak bertipe number. - Function add membangkitkan error jika nilai argumen dari
numBtidak bertipe number.
Apa maksud dari skenario positif dan negatif? Skenario positif berarti kasus pengujian yang mengharapkan keberhasilan output dari program. Misalnya, kita berharap add(1, 2) dapat menghasilkan 3. Berbeda dengan skenario negatif, ia akan memvalidasi kemungkinan error yang dapat terjadi dalam program. Misalnya, kemunculan error ketika parameter function tidak bertipe yang sesuai.
Dengan menuliskan daftar skenarionya, kita akan lebih mudah menguji fungsionalitas aplikasi. Mari kita coba tuangkan kasus pengujian pertama.
import { test } from 'node:test';import assert from 'node:assert';import { add } from './calculator.mjs';test('should add correctly', () => { // Arrange const operandA = 1; const operandB = 1; // Action const actualValue = add(operandA, operandB); // Assert const expectedValue = 2; assert.equal(actualValue, expectedValue);});Pengujian dilakukan menggunakan function test yang diimpor dari node:test. Setiap kali ada kasus pengujian dan akan diuji, kita perlu mendefinisikannya dengan function tersebut. Function test menerima dua parameter, yaitu string sebagai nama pengujian dan callback function yang berisi kode pengujian. Lalu, untuk melakukan validasi nilai dari function, kita gunakan assertion dari module node:assert.
Jika kode di atas dieksekusi, berikut adalah tampak hasil testing dari Terminal.
Satu kasus pengujian dinyatakan lolos oleh Node.js.
Function add dapat mengoperasikan penjumlahan aritmetika dengan baik.- Function add membangkitkan error jika nilai argumen dari numA tidak bertipe number.
- Function add membangkitkan error jika nilai argumen dari numB tidak bertipe number.
Sip! Tentunya, tidak berhenti sampai sini. Kita masih punya dua kasus pengujian tersisa.
Untuk menguji ketahanan aplikasi, biasanya kita perlu sampel data yang lebih bervariasi. Bagaimana jika kita uji dengan nilai string sebagai parameter? Kita tahu bahwa operasi penjumlahan harus dilakukan dengan dua buah operan bertipe number. Jika salah satunya string, operasi yang terjadi malah penggabungan (concatenation). Kita ekspektasikan bahwa program akan mengembalikan error untuk masalah ini.
import { test } from 'node:test';import assert from 'node:assert';import { add } from './calculator.mjs';test('should add correctly', () => { // Arrange const operandA = 1; const operandB = 1; // Action const actualValue = add(operandA, operandB); // Assert const expectedValue = 2; assert.equal(actualValue, expectedValue);});test('should throw an error if string passed on numA parameter', () => { const potentialErrorToBeThrew = () => { // ArrangeIngat, ya, bahwa kasus pengujian didefinisikan dengan function test. Oleh karena itu, kita panggil function test untuk kedua kalinya. Karena ini akan menguji kemunculan error saat string digunakan, kita gunakan assertion bernama throws untuk melakukannya. Ada dua parameter penting untuk assertion tersebut.
- Parameter pertama, kita isikan callback berisi kode yang berpotensi terjadi error exception.
- Parameter kedua, kita berikan objek Error karena nilai tersebut yang diharapkan keluar ketika program terjadi error.
Tidak lain dan sudah jelas bahwa kita akan mendapatkan pesan kegagalan dari automated testing seperti berikut.
Yap, ada kegagalan pengujian. Ini artinya ada yang salah dengan kode program kita atau function add dalam hal ini.
Jangan pusing dahulu dengan error di atas, ya. Agar pengujian berhasil, kita perlu membaca dengan saksama pesan error-nya. Berikut adalah tips dari kami.
- Perhatikan seluruh pengujian gagal berdasarkan namanya. Biasanya diwarnai merah. Dalam hal ini, pengujian yang gagal bernama “should throw an error if string passed on numA parameter”.
- Pesan error biasanya diidentifikasi berdasarkan nama dan pesan error-nya. Biasanya terletak tepat di bawah nama pengujian. Anda bisa perhatikan teks “AssertionError: Missing expected exception” dalam masalah ini.
- Jika masih bingung, cukup perhatikan saja pesan error-nya. Dalam kasus kita, Node.js memberitahu bahwa ternyata kemunculan error exception yang diharapkan tidak terdeteksi.
Nah, dari sini kita paham bahwa function add memiliki celah error dan ini perlu segera diperbaiki. Mari kita perbaiki function-nya sum (lihat berkas calculator.mjs) menjadi seperti berikut.
export function add(numA, numB) { if (typeof numA !== 'number') { throw new Error('Expected a number'); } return numA + numB;}Jika sudah, mari kita jalankan ulang pengujiannya.
Dua skenario selesai.
Function add dapat mengoperasikan penjumlahan aritmetika dengan baik.Function add mengembalikan error jika nilai argumen dari numA tidak bertipe number.- Function add mengembalikan error jika nilai argumen dari numB tidak bertipe number.
Tugas masih belum selesai. Mirip seperti sebelumnya, definisikan skenario pengujian ketiga dengan memanggil function test.
- import { test } from 'node:test';
- import assert from 'node:assert';
- import { add } from './calculator.mjs';
- test('should add correctly', () => {
- // Arrange
- const operandA = 1;
- const operandB = 1;
- // Action
- const actualValue = add(operandA, operandB);
- // Assert
- const expectedValue = 2;
- assert.equal(actualValue, expectedValue);
- });
- test('should throw an error if string passed on numA parameter', () => {
- const potentialErrorToBeThrew = () => {
- // Arrange
- const operandA = '5';
- const operandB = 4;
- // Action
- add(operandA, operandB);
- };
- // Assert
- assert.throws(potentialErrorToBeThrew, Error);
- });
- test('should throw an error if string passed on numB parameter', () => {
- const potentialErrorToBeThrew = () => {
- // Arrange
- const operandA = 10;
- const operandB = '8';
- // Action
- add(operandA, operandB);
- };
- // Assert
- assert.throws(potentialErrorToBeThrew, Error);
- });
Jalankan kode ujinya dan tampaklah error.
Anda sudah tahu apa yang perlu dilakukan selanjutnya, bukan? Mari perbaiki function add kita seperti berikut.
- export function add(numA, numB) {
- if (typeof numA !== 'number' || typeof numB !== 'number') {
- throw new Error('Expected a number');
- }
- return numA + numB;
- }
Sampai di sini, mungkin ada yang bertanya, mengapa validasi dua parameter tidak sekaligus dilakukan? Karena sebisa mungkin program diuji serta diperbaiki sedikit demi sedikit sehingga jika ada masalah atau kesalahan, pemecahan dan identifikasi lebih mudah dan terstruktur.
import { test } from 'node:test';import assert from 'node:assert';import { add } from './calculator.mjs';test('should add correctly', () => { // Arrange const operandA = 1; const operandB = 1; // Action const actualValue = add(operandA, operandB); // Assert const expectedValue = 2; assert.equal(actualValue, expectedValue);});test('should throw an error if string passed on numA parameter', () => { const potentialErrorToBeThrew = () => { // ArrangeKini semuanya sudah kembali hijau.
Yeay! Mari kita perbarui kondisi daftar skenario pengujian kita.
- Function add dapat mengoperasikan penjumlahan aritmetika dengan baik.
- Function add membangkitkan error jika nilai argumen dari numA tidak bertipe number.
- Function add membangkitkan error jika nilai argumen dari numB tidak bertipe number.
Mission completed! Apakah Anda mengerti makna dari penyelesaian semua ini? Ada dua hal besar yang kita peroleh.
- Program kita sudah terbebas dari bug.
- Pengujian dilakukan tanpa menjalankan program sama sekali.
Maknanya, kita sudah berhasil mencapai dan merasakan esensi dari pengujian otomatis.
Grup Kasus Pengujian
Tidak menutup kemungkinan fitur aplikasi akan selalu meningkat seiring berjalannya waktu. Kita tidak tahu akan sebesar dan sebanyak apa. Hal yang pasti, jumlah kode pengujian berbanding lurus dengan jumlah fitur aplikasi. Ada satu hal yang bisa kita lakukan untuk meningkatkan kualitas pengujian, yaitu membuat kategori terhadap skenario pengujian.
Perhatikan kode berikut.
- import { describe, test } from 'node:test';
- import assert from 'node:assert';
- import { add } from './calculator.mjs';
- describe('Calculator', () => {
- test('should add correctly', () => {
- /* ...kode disembunyikan… */
- });
- test('should throw an error if string passed on numA parameter', () => {
- /* ...kode disembunyikan… */
- });
- test('should throw an error if string passed on numB parameter', () => {
- /* ...kode disembunyikan… */
- });
- });
Kita menambahkan function describe untuk membungkus seluruh test case (kasus pengujian). Ini berguna untuk mengategorikan banyak kasus pengujian sehingga kode makin rapi dan lebih mudah dipelihara. Jika testing dijalankan, kita akan melihat pengategorian testing.
Jadi, jika ingin menguji fitur berbeda, kita bisa memisahkan dengan describe yang berbeda.
Aliases
Sebagian developer lebih menyukai it daripada test ketika mendefinisikan test case. Keduanya memiliki tujuan yang identik. Dengan it, pembacaan kasus pengujian menjadi lebih gampang.
| it('should add correctly') ⇒ itu harus ditambahkan dengan benar. |
Ini hanya persoalan penamaan saja. Selain alias tersebut, describe juga memiliki kembaran, yaitu suite. Namun, describe dan it merupakan pasangan yang serasi, bukan?
Jika disesuaikan lagi, berikut adalah hasil akhir dari latihan kita. Anda pun bisa bereksplorasi lebih jauh tentang automated test menggunakan interactive code tersebut.
import { describe, it } from 'node:test';import assert from 'node:assert';import { add } from './calculator.mjs';describe('Calculator', () => { it('should add correctly', () => { // Arrange const operandA = 1; const operandB = 1; // Action const actualValue = add(operandA, operandB); // Assert const expectedValue = 2; assert.equal(actualValue, expectedValue); }); it('should throw an error if string passed on numA parameter', () => { const potentialErrorToBeThrew = () => {Dengan demikian, kita sudah belajar dua module inti milik Node.js untuk melaksanakan pengujian otomatis. Sebetulnya, masih banyak fungsi yang belum kita ulik di sana. Namun, kami sarankan Anda untuk bisa memahami dan membaca dokumentasi resmi dari Node.js.
Testing dalam Bun
Pada materinya, kita sudah belajar bermain pengujian otomatis dengan Node.js. Bun sebagai runtime alternatif dari Node.js juga memiliki built-in module untuk menangani kode pengujian, tetapi dengan sedikit perbedaan. Mari kita pelajari.
Mendefinisikan testing dengan Bun mirip rasanya seperti menggunakan Jest. Bagi yang belum kenal, Jest adalah testing framework yang menawarkan lebih banyak kebutuhan perihal pengujian otomatis yang tidak ditawarkan oleh native API, seperti dari Node.js atau Bun. Bun memang ingin menyesuaikan gaya penulisan testing dengan framework terkenal itu agar para developer yang terbiasa dengannya tidak perlu pusing mempelajari sintaks baru. Ini keputusan yang baik, kan?
Module test dari Bun terletak pada bun:test. Seluruh kebutuhan terkait testing ada di sana, baik test runner maupun test assertion. Contoh implementasinya seperti berikut.
- import { it, describe, expect } from 'bun:test';
- describe('arithmetic', () => {
- it('1 + 1', () => {
- expect(2 + 2).toBe(4);
- });
- it('3 * 2', () => {
- expect(3 * 2).toBe(6);
- });
- });
bun:test sedikit berbeda dengan node:test. Jika Node.js menggunakan assert untuk menguji nilai, Bun menggunakan expect dan matcher. expect menerima satu parameter yang menjadi actual value dan kita membutuhkan .toBe sebagai matcher untuk mengujinya dengan expected value. Bagaimana dengan describe dan it? Mereka mirip dengan Node.js, kok.
Penasaran dengan tampak hasil pengujiannya? Cukup jalankan perintah bun test di terminal dan hasil akan terlihat seperti ini.
Tantangan
Tentunya Anda tidak lupa dengan kalkulator, kan? Kami menantang Anda untuk mengubah sintaks kode pengujian yang telah kita susun menggunakan Node.js agar bisa dijalankan oleh Bun. Ini menarik sekali! Silakan dicoba.
Bila sudah merasa mentok, Anda bisa melihat hasilnya pada tab Hasil berikut.
Lakukan konversi sintaks kode pengujian dari proyek kalkulator agar kompatibel dengan Bun.
Silakan buka tab Hasil jika merasa buntu.
Strategi Terbaik Susun Testing
Kita sudah selesai mempelajari praktik pengujian otomatis. Pelajaran penting yang kita dapat adalah menguji program tanpa menjalankannya sama sekali. Cukup bermodalkan module test dan runtime seperti Node.js atau Bun. Selamat! Anda sudah melalui materinya dengan baik.
Sebelum Anda memasuki kuis, kami perlu membahas beberapa hal yang menjadi praktik terbaik (best practice) agar kode testing makin berkualitas.
Tentukan Konteks, Skenario, dan Ekspektasi
Jika masih ingat dengan kasus pengujian sebelumnya, pasti menemukan bahwa kita selalu memberikan nilai string pada parameter pertama, baik pada describe, it, maupun test. Ini adalah nama dari kasus pengujian yang sedang berusaha kita definisikan. Sebagai pembaca kode, developer akan memahami bagian dan hal yang sedang diuji pada program. Tidak hanya itu, pembaca hasil pengujian pun akan menjadi mudah ketika melihat hasil pengujiannya dalam output Terminal/CMD.
- import { it, describe, expect } from 'bun:test';
- describe('Fitur 1', () => {
- it('harus dapat A', () => {
- expect('A').toBe('A');
- });
- it('harus dapat B', () => {
- expect('B').not.toBe('A');
- });
- });
Mengikuti Pola AAA
Ada satu pola atau konvensi yang bisa kita terapkan agar pembacaan kode testing makin mudah. Itu adalah pola AAA atau Arrange, Action & Assert. Ketiga A ini adalah sebuah section atau bagian untuk memisahkan kode dalam satu test case guna memudahkan pemahaman pembaca.
- A pertama - Arrange: persiapan kode untuk sebelum melakukan eksekusi pengujian fitur. Misalnya, menyiapkan angka yang akan digunakan untuk penjumlahan.
- A kedua - Action: eksekusi fitur terjadi dalam bagian ini.
- A ketiga - Assert: memastikan antara actual value dan expected value sudah sesuai.
Seharusnya Anda memperhatikan dengan baik latihan kasus kita bahwa terdapat tiga komentar yang berisi AAA. Berikut contoh implementasinya.
- import { describe, it, expect } from 'bun:test';
- function add(numA, numB) {
- return numA + numB;
- }
- describe('Calculator', () => {
- it('should add correctly', () => {
- // Arrange
- const operandA = 1;
- const operandB = 1;
- // Action
- const actualValue = add(operandA, operandB);
- // Assert
- const expectedValue = 2;
- expect(actualValue).toBe(expectedValue);
- });
- });
Definisikan Skenario Positif dan Negatif
Jumlah kasus pengujian berbanding lurus dengan jumlah fitur dalam aplikasi. Ada istilah bernama edge cases, yaitu situasi yang tidak biasa dan jarang terjadi, tetapi bisa menyebabkan masalah jika tidak diantisipasi dengan baik. Edge case dapat mengeluarkan bug fatal yang telah kita lihat sebelumnya. Contoh sederhananya adalah sebuah function bernama add sudah selayaknya menerima parameter number. Bagaimana jika kita beri situasi yang tidak umum seperti menggunakan string? Hasilnya akan string, bukan number. Tepat sekali!
Pendekatan yang kami sarankan adalah mulailah membangun skenario-skenario pengujian secara lengkap dengan skenario negatif lebih dahulu dan diikuti dengan skenario positif. Dengan cara seperti ini, kita akan lebih bisa fokus dalam menemukan berbagai edge case. Mengatasi berbagai edge case membantu memastikan bahwa aplikasi atau sistem lebih stabil dan dapat diandalkan dalam berbagai situasi ekstrem.
Rangkuman Code Quality
Sudah saatnya kita membaca rangkuman terhadap materi-materi yang telah dipelajari. Gunakanlah rangkuman ini sebaik-baiknya untuk menghadapi kuis nanti.
Type System dalam JavaScript
Sistem pengecekan tipe dalam JavaScript dikategorikan dalam dynamic type. Artinya, waktu pemeriksaan tipe terjadi pada saat program berjalan (runtime) dan memungkinkan untuk berubah dari satu tipe data ke lainnya. Ini mirip dengan interpreter, tetapi berbeda secara konsep.
Bahasa-bahasa pemrograman yang bertipe static akan memiliki proses pemeriksaan pada tahap compile. Ini memberikan keamanan yang lebih dalam konteks bug. Contohnya seperti Java, C++, Golang, Rust, dsb.
Tidak hanya dynamic, JavaScript juga tergolong weakly-typed language. Ini disebabkan karena fitur type coercion yang memungkinkan konversi otomatis pada suatu pemrosesan dua buah nilai. Contohnya, harapan keluarnya nilai number dari hasil penjumlahan aritmetika yang ternyata berujung pada hasil string karena dilakukan penggabungan.
- function add(numA, numB) {
- return numA + numB;
- }
- console.log(add(1, 1)); // 2
- console.log(add(3, 2)); // 5
- console.log(add('5', 4)); // "54"
JSDoc, Alat Dokumentasi Kode
Konsekuensi dari bahasa yang bertipe dynamic dan weakly-typed adalah menghasilkan bug. Salah satu cara untuk mengatasi masalah ini dapat dengan memanfaatkan JSDoc sebagai alat dokumentasi kode. Kita dapat mendeskripsikan kode dengan memanfaatkan fitur komentar pada JavaScript. Ini disebut dengan JSDoc comment.
Setiap JSDoc comment harus dimulai dengan /**. Komentar yang menggunakan /*, /***, atau lebih dari tiga bintang (*) tidak akan diproses oleh JSDoc.
- /** Say hello to world */
- function greet() {
- console.log('Hello, world!');
- }
Dokumentasi di atas dapat kita tulis langsung dalam source code. Kerennya, JSDoc dapat menghasilkan dokumentasi dalam bentuk halaman web.
Flow, Type Check Library
Selain menggunakan fitur komentar, ada tools lain yang relatif lebih baik untuk diterapkan. Tools ini adalah Flow. Flow memungkinkan developer dapat mendefinisikan tipe data langsung dalam kode programnya. Misalnya, saat kita mendeklarasikan variabel, parameter function, return value function, dsb.
- const myName: string = 'Flow';
- function greet(name: string) {
- console.log(`Hello, ${name}. My name is ${myName}`);
- }
- greet('JavaScript');
Cukup tambahkan titik dua (:) dan diikuti dengan tipe nilainya (type) untuk mendefinisikan tipe data. Dalam Flow, ini dinamakan static type annotations. Flow seakan-akan dapat menambahkan type check layer pada JavaScript sebelum akhirnya kode dieksekusi oleh mesin. Namun, sebelum mulai dieksekusi, kita perlu menghilangkan static type annotations milik Flow karena ini bukan standar dari JavaScript tentunya.
TypeScript, Superset dari JavaScript
Jika Flow adalah sebuah library yang dapat menambahkan type check layer, ada tools lain berbentuk bahasa pemrograman yang memiliki type check layer secara bawaan, yaitu TypeScript.
TypeScript dikembangkan oleh Microsoft dengan memadukan seluruh fitur JavaScript dengan bahasa yang lebih ketat. Saat ini, ia menjadi bahasa yang paling digemari di dunia berdasarkan survei dari Stackoverflow. Secara penulisan, ia sangat mirip dengan Flow sehingga kita tidak perlu mempelajari cara baru.
- const myName: string = 'TypeScript';
- function greet(name: string) {
- console.log(`Hello, ${name}. My name is ${myName}`);
- }
- greet('JavaScript');
Penulisan kode TypeScript membutuhkan .ts sebagai ekstensi berkasnya. Selain itu, kode di atas juga perlu diproses agar menjadi kode JavaScript yang valid. Compiler yang digunakan untuk TypeScript adalah tsc. Ia akan memeriksa sekaligus menghasilkan berkas .js yang siap eksekusi.
Style Guide
Salah satu hal penting dalam sektor kegiatan menulis kode atau coding adalah seni. Dalam konteks ini, seni yang dimaksud adalah tingkat konsistensi kode yang ditulis. Meskipun sebetulnya hal tersebut tidak berpengaruh terhadap validitas sintaks kode JavaScript. Ini kita sebut sebagai style guide.
Beberapa contoh terkait style guide adalah
- penggunaan tanda titik-koma (semicolon),
- gaya nama variabel,
- penempatan komentar dalam kode,
- penggunaan block-code (curly-bracket), seperti dalam percabangan,
- banyak assignment secara sekaligus, dan masih banyak lainnya lagi.
Beberapa hal di atas adalah contoh-contoh gaya penulisan yang menurut kami banyak terjadi variasi antar developer. Ada beberapa dampak yang sangat mungkin terjadi jika konvensi penulisan kode JavaScript tidak segera ditetapkan.
- Kode sulit dibaca dan dipelajari oleh sesama kolega developer sehingga produktivitas menurun.
- Kolaborasi menjadi sulit terjalin dengan baik, apalagi saat mengalami penggabungan dua buah perubahan kode dari developer yang berbeda.
- Jika ada developer baru, proses onboarding pun akan membutuhkan waktu lebih untuk memahami kode.
Code Convention dengan Linter Library
Penentuan style guide JavaScript sesegera mungkin dapat meningkatkan kualitas penulisan kode. Salah satu cara yang dapat membangun pembentukan style guide dengan baik dan cepat adalah penggunaan library, seperti ESLint. ESLint dapat memberikan feedback atas hasil analisisnya terhadap penulisan kode kita. Feedback ini akan diberi melalui Terminal/CMD.
Contoh feedback yang diberikan ESLint seperti berikut.
Cara pembuatan aturan penulisan dalam ESLint dilakukan melalui konfigurasi yang bernama rule. Ada tiga kategori yang tersedia pada setiap rule-nya berdasarkan tingkat keparahan.
- “off” atau 0: aturan tersebut tidak dipermasalahkan atau dimatikan.
- “warn” atau 1: aturan ditetapkan sebagai peringatan saja saat dilanggar.
- “error” atau 2: aturan wajib dipatuhi dan program dapat mengalami error.
Contoh bentuk penulisan aturan dalam konfigurasi tampak seperti berikut.
- {
- rules: {
- "no-duplicate-imports": "off",
- "no-use-before-define": "error",
- "constructor-super": "error",
- "no-var": "warn",
- "no-unreachable": "warn",
- "no-extra-boolean-cast": "warn"
- }
- }
Pengujian Program
Sebagai pembangun program, kami yakin Anda tidak asing dengan istilah testing atau pengujian. Pengujian adalah proses memastikan keberhasilan suatu sistem untuk mencegah kegagalan saat beroperasi. Ada dua metode yang bisa kita lakukan. Jika tidak secara manual, kita akan lakukan pengujian aplikasi secara otomatis.
Pengujian manual cukup dilakukan dengan menjalankan program dan memperhatikan output yang dikeluarkan. Perbaikan akan dilakukan jika terjadi error. Namun, testing otomatis sangat kami rekomendasikan karena cara manual sangat rentan dilakukan.
Pengujian otomatis akan memberikan dua kemungkinan hasil, yaitu pass dan fail. Jika pengujian dinyatakan lolos, runtime akan menandainya sebagai pass (biasanya diwarnai hijau), sedangkan runtime memberi hasil fail (biasanya diwarnai merah) saat pernyataan gagal muncul.
Kultur yang tidak akan kita dapati dengan pengujian otomatis: jalankan program → saksikan hasil → perbaiki kode jika hasil aneh.
Testing dalam Node.js
Pengujian pada Node.js membutuhkan dua buah module, yaitu node:test dan node:assert. node:test berperan sebagai test runner yang menawarkan API untuk menuliskan skenario pengujian. Adapun node:assert berperan sebagai test assertion yang menyediakan objek untuk memvalidasi nilai antara actual (nilai sesungguhnya) dan expected (nilai yang diharapkan).
Berikut adalah contoh kode testing dengan Node.js.
- import { describe, it } from 'node:test';
- import assert from 'node:assert';
- import { add } from './calculator.mjs';
- describe('Calculator', () => {
- it('should add correctly', () => {
- // Arrange
- const operandA = 1;
- const operandB = 1;
- // Action
- const actualValue = add(operandA, operandB);
- // Assert
- const expectedValue = 2;
- assert.equal(actualValue, expectedValue);
- });
- });
Berikut adalah beberapa penjelasan dari beberapa function di atas.
- describe: membungkus banyak kasus pengujian dalam konteks yang sama.
- test: mendefinisikan kasus pengujian baru.
- assert.equal: memvalidasi nilai antara actual dan expected.
Testing dalam Bun
Function testing milik Bun sebetulnya tidak berbeda dengan Node.js. Namun, lokasi module-nya perlu kita sesuaikan menjadi bun:test. Contoh implementasinya seperti berikut.
- import { it, describe, expect } from 'bun:test';
- describe('arithmetic', () => {
- it('1 + 1', () => {
- expect(2 + 2).toBe(4);
- });
- it('3 * 2', () => {
- expect(3 * 2).toBe(6);
- });
- });
bun:test sedikit berbeda dengan node:test. Jika Node.js menggunakan assert untuk menguji nilai, Bun menggunakan expect dan matcher. expect menerima satu parameter yang menjadi actual value dan kita membutuhkan .toBe sebagai matcher untuk mengujinya dengan expected value.
Strategi Terbaik Susun Testing
Berikut adalah tiga best practice dalam menulis kode testing otomatis.
- Menentukan konteks, skenario, dan ekspektasi melalui parameter pertama dari describe, it, ataupun test.
- Menerapkan strategi arrange, action, dan assert dalam membangun proses validasi nilai. Strategi ini disingkat dengan AAA.
- Menguji hasil program berdasarkan daftar edge cases-nya. Caranya bisa dengan mendefinisikan skenario-skenario yang bersifat positif dan negatif dan menuangkannya dalam kode testing.
- Get link
- X
- Other Apps














Comments
Post a Comment