Unit Testing yang Isinya Bukan Cuma Unit Test
Satu script per tier testing: vet, integration MySQL asli, smoke gate, sampai E2E Playwright.
Ringkasan
Dulu push kode ribet harus ketik command panjang manual suka ada yang kelupaan pas buru-buru. Sekarang semua diringkas jadi empat script backend frontend e2e sama smoke yang otomatis berhenti kalau ada yang gagal. Ditambah ada panduan jelas di README kapan harus jalanin tes yang mana jadi nggak nebak-nebak lagi.
Dulu, tiap mau push kode, yang saya ketik itu rentetan panjang: go test -tags=integration -p 1 ./..., nyalain Docker dulu buat db-test, vitest run, kadang Playwright dengan flag panjang. Urutannya suka beda-beda tergantung buru-buru atau nggak. Dan pas lagi kejar deadline, langkah yang kelewat biasanya justru yang paling penting.
Awalnya saya kira solusinya dokumentasi yang lebih rapi. Ternyata dokumentasi cuma jadi hiasan kalo eksekusinya masih manual. Ada juga ironi kecil di nama folder: unit_testing, padahal isinya nggak cuma unit test. Empat tier sekaligus ada di situ: unit, integration, E2E, dan smoke.
Commit ini menuang semuanya jadi empat script: satu buat backend, satu buat frontend, satu buat E2E, satu buat smoke. Semuanya dikepalai set -euo pipefail [1]. Begitu ada satu pipeline balik status non-zero, shell langsung berhenti.
Nggak ada lagi cerita script jalan terus padahal step sebelumnya udah gagal.
Integration yang nyambung ke MySQL beneran
Bagian yang paling sering dilupakan pas ngetik manual itu prasyaratnya. Di run_backend.sh, Docker dicek dulu sebelum integration jalan:
if docker info >/dev/null 2>&1; then
docker compose -f docker-compose.test.yml up -d db-test
else
log "Docker not running - integration tests will skip"
fi
go test -tags=integration -p 1 ./...
docker compose up -d sifatnya idempoten: service yang udah jalan nggak di-start ulang, dan kalo config berubah dia yang nge-recreate [5]. Script ini jadi aman dipanggil berulang tanpa takut kontainer hidup ganda.
Flag -p 1 juga bukan gaya-gayaan. Semua package integration berbagi satu DB test MySQL 5.7 asli (SQLite dilarang keras), dan -p yang ngatur berapa test binary boleh jalan paralel, defaultnya GOMAXPROCS [3]. -parallel itu beda flag: dia cuma berlaku di dalam satu test binary [4]. Salah paham dua flag ini bikin orang ngeyel udah ngubah setting padahal salah lubang.
Smoke sebagai gerbang deploy
run_smoke.sh jawaban buat pertanyaan "udah deploy, sekarang gimana". Dia ngecek tiga URL: health endpoint, root halaman, dan root API. Yang terakhir harus JSON valid, bukan cuma kode 200:
curl -sf -m 10 "$BASE/health" >/dev/null || fail "/health not 200"
body=$(curl -sf -m 10 "$BASE/api/v1/") || fail "/api/v1/ not 200"
printf '%s' "$body" | python3 -c "import json,sys; json.load(sys.stdin)" || fail "/api/v1/ is not valid JSON"
Satu aja gagal, exit 1, alur deploy berhenti di situ. Defaultnya nunjuk server lokal, dan tinggal lempar URL staging sebagai argumen pertama. Smoke yang dulu berarti "cek manual pake browser" sekarang jadi gerbang yang bisa dicangkulin di akhir alur deploy.
Untuk E2E, run_e2e.sh isinya tipis: npx playwright test "$@". Justru itu poinnya. Semua flag Playwright tetap bisa dipakai, termasuk njalanin satu file spec aja. Yang saya atur di config ya retry-nya: Playwright secara default nggak nge-retry test yang gagal [2], dan trace diset on-first-retry jadi file trace cuma direkam pas percobaan kedua [6]. Debug tetap dapat data, storage nggak berubah jadi tumpukan.
README yang dijaga kayak kontrak
Bagian yang nilai paling nentu malah bukan script-nya, tapi tabel kapan-jalankan-apa di README.
Tiap save, jalanin backend mode unit aja. Sebelum commit, ditambah frontend lint. CI atau push tag, full suite. Post-deploy, smoke. Pre-release, E2E. Jadi pas ada yang nanya "sebelum merge jalanin yang mana", jawabannya udah nggak terserah lagi.
Saya tutup pakai standar yang sama kayak README-nya: kalo run_backend.sh hijau, artinya kode lolos kontrak yang sama dengan yang dipakai orang lain. Bukan cuma hijau di laptop saya.
Sources
[1] https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
[2] https://github.com/microsoft/playwright/blob/main/docs/src/test-retries-js.md
[3] https://github.com/golang/go/blob/release-branch.go1.23/src/cmd/go/internal/work/build.go
[4] https://github.com/golang/go/blob/release-branch.go1.23/src/cmd/go/internal/test/test.go
[5] https://github.com/docker/compose/blob/main/docs/reference/docker_compose_up.yaml
[6] https://github.com/microsoft/playwright/blob/main/docs/src/test-configuration-js.md