Menguasai strategi testing dan debugging modul WebAssembly: cargo test untuk target wasm, wasm-bindgen-test, browser testing dengan Playwright, serta wasm-tools print/dump, DWARF + LLDB/Chrome DevTools, dan section name

Setelah di episode 13 kalian membangun modul Wasm untuk data processing — pada episode ini kita membahas hal yang sering dianggap membosankan namun menentukan produksi: testing dan debugging. Modul Wasm punya tantangan unik: ia berjalan di host yang berbeda (Node, browser, edge), interop-nya melintasi batas bahasa, dan bytecode-nya tidak mudah "diinspect" seperti kode sumber biasa.
Mengapa topik ini penting? Karena bug di interop Wasm–JS adalah salah satu sumber frustrasi terbesar: kesalahan tipe, salah offset memory, atau data race antar bahasa. Tanpa strategi testing dan tool debugging yang tepat, masalah-masalah ini berubah menjadi misteri yang memakan waktu berhari-hari.
Logika murni — fungsi yang tidak bergantung pada wasm-bindgen — bisa diuji sebagai unit test biasa:
cargo test#[wasm_bindgen]
pub fn fib(n: u32) -> u32 {
match n {
0 | 1 => n,
_ => fib(n - 1) + fib(n - 2),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fib_sederhana() {
assert_eq!(fib(0), 0);
assert_eq!(fib(1), 1);
assert_eq!(fib(10), 55);
}
}Logika diuji cepat di host native (ratusan ms). Ini lapisan pertama dan termurah.
Untuk fungsi yang bergantung pada interop — string, JsValue, memory — pakai wasm-bindgen-test, yang menjalankan test di browser/Node sebagai Wasm:
cargo add --dev wasm-bindgen-testuse wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn sapaan_benar() {
assert_eq!(sapaan("Budi"), "Halo, Budi!");
}
#[wasm_bindgen_test]
async fn async_interop() {
let hasil = js_sys::Promise::resolve(&JsValue::from(42)).await;
assert_eq!(hasil, JsValue::from(42));
}wasm-pack test --headless --chromeTest ini menjalankan modul Wasm yang sesungguhnya di engine browser — menguji interop persis seperti kondisi produksi.
Untuk menguji seluruh aplikasi — termasuk modul Wasm yang dipanggil dari UI — gunakan Playwright. Ini menguji "user journey", bukan hanya unit:
import { test, expect } from "@playwright/test"
test("aplikasi memakai modul wasm", async ({ page }) => {
await page.goto("/")
await page.fill("#angka", "20")
await page.click("#hitung")
await expect(page.locator("#hasil")).toHaveText("6765")
})npx playwright testPlaywright memuat halaman nyata di Chromium/Firefox/WebKit, menunggu init() selesai, dan memverifikasi output — menangkap masalah yang tidak terlihat di unit test: salah inisialisasi, timing, dan interaksi DOM.
Ketika masalah ada di dalam modul, mulai dari pembacaan struktur:
wasm-tools print modul.wasmOutput WAT menunjukkan fungsi, imports, exports, dan instruksi secara persis — untuk memverifikasi apa yang benar-benar dikompilasi compiler.
Untuk inspeksi raw binary:
wasm-tools dump modul.wasmdump menampilkan setiap section — type, function, code, data — plus ukuran dan offset-nya. Berguna untuk mendiagnosis masalah ukuran atau section yang korup.
Compiler modern bisa menyertakan informasi DWARF ke dalam modul Wasm — metadata yang memetakan bytecode kembali ke kode sumber.
cargo build --target wasm32-unknown-unknown --features debugChrome DevTools punya debugger Wasm source-level: dengan DWARF, kalian bisa memasang breakpoint di kode Rust/C asli, melangkah per baris, dan menginspeksi variabel — persis seperti debugging native di IDE.
LLDB (dengan support Wasm dari Wasmtime ecosystem) bisa men-debug modul di sisi server:
wasmtime run --debug modul.wasmname: Nama untuk ReadabilityModul yang di-minify tanpa nama fungsi sangat sulit di-debug. Compiler dapat menyertakan custom section name yang menyimpan nama fungsi dan parameter:
RUSTFLAGS="-C symbol-mangling-version=v0" cargo build --releasewasm-tools print modul.wasmOutput print akan menampilkan (func $nama_fungsi_asli ...) alih-alih (func (;0;) ...) — perbedaan antara "membaca hieroglif" dan "membaca kode". Kebiasaan kecil yang menyelamatkan waktu debugging berjam-jam.
Tip
Jadikan pipeline tiga lapis sebagai default: unit test host (cargo test) untuk logika, wasm-bindgen-test untuk interop, dan Playwright untuk aplikasi utuh. Setiap lapisan menangkap kelas bug yang berbeda dengan biaya yang naik bertahap.
cargo test native bisa gagal di browser karena perbedaan ABI; selalu ada satu lapis test interop.panic! di Rust berubah menjadi trap Wasm; aktifkan hook yang mengubahnya menjadi error yang bisa dibaca.f32/f64 hampir selalu butuh epsilon; assert_eq! langsung menghasilkan kegagalan menyesatkan.Warning
Untuk modul yang mem-parse input eksternal, tambahkan fuzz testing (mis. cargo-fuzz) di lapisan unit test: hasilnya tidak boleh trap atau menghabiskan memory pada input acak. Bug parsing adalah kelas bug paling umum di runtime produksi.
Pada episode 14 ini, kalian telah membangun strategi testing dan debugging untuk modul WebAssembly.
Inti yang harus dibawa pulang:
cargo test (host), interop dengan wasm-bindgen-test, aplikasi dengan Playwright.wasm-tools print/dump untuk inspeksi struktur modul.name dan build release yang konsisten menjaga modul tetap bisa dibaca.Di episode 15 selanjutnya kita akan membahas optimasi ukuran & performa — wasm-opt dari Binaryen, wasm-strip, --release dan opt-level="z", code splitting dengan lazy instantiation, benchmark, dan menghindari alloc di hot-path. Sampai jumpa di episode 15!