September 28, 2026

Dasar Testing.

Konsep dasar automated testing: unit, integration, dan end-to-end test, piramida testing, pola Arrange-Act-Assert, test double (mock, stub, spy, fake), serta cara menulis test yang andal dan mudah dirawat.

Automated test adalah kode yang memeriksa kode lain. Tujuannya bukan sekadar “coverage tinggi”, melainkan kepercayaan diri: berani refactor, upgrade dependency, atau deploy hari Jumat tanpa takut ada yang rusak diam-diam. Catatan ini membahas konsep yang berlaku di bahasa apa pun; implementasinya ada di catatan Vitest , Pest untuk Laravel , dan Playwright .


1. Jenis Test

JenisMengujiKecepatanContoh
UnitSatu fungsi/class secara terisolasiMilidetikhitungDiskon(100000, 10) → 90000
IntegrationBeberapa bagian bekerja bersama (kode + DB, kode + API)Puluhan–ratusan msEndpoint POST /orders menyimpan ke database
End-to-end (E2E)Aplikasi utuh dari sudut pandang user, lewat browserDetikLogin → tambah ke keranjang → checkout

Istilah lain yang sering muncul:

IstilahArti
Feature testIstilah Laravel untuk integration test yang memanggil HTTP request
Component testMerender satu komponen UI dan berinteraksi dengannya
Smoke testTest kecil untuk memastikan hal paling dasar jalan (halaman utama 200)
Regression testTest yang ditulis setelah bug ditemukan, agar bug itu tidak kembali
Snapshot testMembandingkan output dengan hasil yang tersimpan sebelumnya

2. Piramida Testing

          ▲  sedikit, lambat, mahal, paling realistis
         / \
        / E2E\
       /-------\
      /Integrat.\
     /-----------\
    /    Unit     \
   /_______________\
      banyak, cepat, murah, paling terisolasi

Idenya: banyak unit test (murah dan cepat), sedang integration test, dan sedikit E2E untuk alur kritis (login, pembayaran). Makin ke atas, test makin realistis tapi makin lambat dan rapuh.

Varian modern seperti testing trophy menekankan bahwa integration test memberi rasio “kepercayaan per biaya” terbaik untuk aplikasi web. Dokumentasi Laravel pun menyatakan sebagian besar test sebaiknya berupa feature test. Kesimpulan praktisnya:

  • Logika murni (kalkulasi, parsing, validasi) → unit test.
  • Endpoint, query, dan komponen UI → integration/feature test.
  • 3–10 alur bisnis terpenting → E2E.

3. Pola Arrange–Act–Assert (AAA)

Setiap test yang baik punya tiga bagian jelas:

// hitungTotal.ts
export function hitungTotal(items: { harga: number; qty: number }[], diskonPersen = 0) {
  const subtotal = items.reduce((t, i) => t + i.harga * i.qty, 0);
  return Math.round(subtotal * (1 - diskonPersen / 100));
}
// hitungTotal.test.ts
import { describe, expect, it } from "vitest";
import { hitungTotal } from "./hitungTotal";

describe("hitungTotal", () => {
  it("menerapkan diskon persen pada subtotal", () => {
    // Arrange — siapkan data
    const items = [
      { harga: 50000, qty: 2 },
      { harga: 25000, qty: 1 },
    ];

    // Act — jalankan satu aksi
    const total = hitungTotal(items, 10);

    // Assert — periksa hasil
    expect(total).toBe(112500);
  });

  it("mengembalikan 0 untuk keranjang kosong", () => {
    expect(hitungTotal([])).toBe(0);
  });
});

Padanannya di BDD adalah Given–When–Then. Aturan penting: satu test = satu perilaku. Nama test menjelaskan perilaku itu dalam kalimat, sehingga laporan gagal langsung terbaca (“hitungTotal › menerapkan diskon persen pada subtotal”).


4. Test Double: Mock, Stub, Spy, Fake

Unit test harus cepat dan deterministik, jadi dependensi yang lambat atau tak terduga (API luar, email, waktu, random) diganti dengan test double.

JenisFungsiContoh
StubMengembalikan jawaban tetapgetKurs() selalu 15500
SpyMencatat pemanggilan fungsi asli/pengganti“apakah kirimEmail dipanggil 1×?”
MockStub + ekspektasi tentang cara dipanggilHarus dipanggil dengan argumen X
FakeImplementasi ringan yang benar-benar bekerjaRepository in-memory, Mail::fake() di Laravel

Contoh dengan dependency injection (paling mudah di-test):

type Mailer = { kirim(to: string, subjek: string): Promise<void> };

export async function daftarUser(email: string, mailer: Mailer) {
  // ... simpan user
  await mailer.kirim(email, "Selamat datang!");
  return { email };
}
import { expect, it, vi } from "vitest";
import { daftarUser } from "./daftarUser";

it("mengirim email sambutan", async () => {
  const mailer = { kirim: vi.fn().mockResolvedValue(undefined) };

  await daftarUser("[email protected]", mailer);

  expect(mailer.kirim).toHaveBeenCalledOnce();
  expect(mailer.kirim).toHaveBeenCalledWith("[email protected]", "Selamat datang!");
});

Kapan jangan mock

  • Jangan mock yang Anda uji. Jika semua dependensi di-mock, test hanya membuktikan mock-nya benar.
  • Utamakan fake daripada mock untuk hal seperti database — database test sungguhan (SQLite in-memory, container) jauh lebih meyakinkan.
  • Mock di batas sistem: HTTP ke pihak ketiga, email, payment gateway, waktu (vi.useFakeTimers()), random.

5. Ciri Test yang Baik (FIRST)

HurufPrinsipArtinya
FFastSuite unit selesai dalam detik, agar dijalankan sering
IIsolatedTidak bergantung urutan atau hasil test lain
RRepeatableHasil sama di laptop, CI, jam berapa pun
SSelf-validatingLulus/gagal jelas, tanpa cek manual log
TTimelyDitulis bersamaan dengan kode (atau sebelumnya — TDD)

Selain itu: uji perilaku, bukan implementasi. Test yang memeriksa state internal atau urutan pemanggilan private method akan patah setiap refactor, padahal perilaku tidak berubah.

TDD singkat

Siklus Red → Green → Refactor: tulis test yang gagal, tulis kode minimal agar lulus, lalu rapikan. Tidak wajib, tapi sangat efektif untuk logika bisnis dengan aturan jelas dan untuk memperbaiki bug (tulis test yang mereproduksi bug dulu).


6. Menjalankan Test di CI

Test paling berharga jika dijalankan otomatis di setiap push/PR. Pipeline minimal: install → lint → type-check → unit/integration → E2E (opsional di branch utama). Contoh GitHub Actions ada di catatan TypeScript di Proyek Nyata . Branch protection yang mewajibkan CI hijau sebelum merge adalah kebiasaan yang sangat layak diadopsi.


Kesalahan Umum

  1. Mengejar coverage 100%. Coverage mengukur baris yang dieksekusi, bukan yang diverifikasi. Test tanpa assertion tetap menaikkan coverage.
  2. Test saling bergantung. Test B lulus hanya jika test A jalan dulu → gagal acak saat dijalankan paralel atau terfilter.
  3. Over-mocking. Semua di-mock, test hijau, produksi tetap rusak.
  4. Test yang flaky dibiarkan. Test yang kadang gagal membuat tim terbiasa mengabaikan merah. Perbaiki atau karantina segera.
  5. Bergantung waktu/random nyata. new Date() dan Math.random() membuat hasil berubah; kendalikan dengan fake timer atau injeksi.
  6. Nama test tidak deskriptif (test1, it works). Saat gagal, tidak ada yang tahu apa yang rusak.
  7. Terlalu banyak E2E. Suite E2E 40 menit membuat developer berhenti menjalankannya.

Ringkasan

  • Unit untuk logika murni, integration/feature untuk sebagian besar aplikasi, E2E untuk alur kritis.
  • Struktur setiap test dengan Arrange–Act–Assert, satu perilaku per test.
  • Mock di batas sistem; utamakan fake dan database test sungguhan.
  • Uji perilaku, bukan implementasi, dan jalankan semuanya di CI.

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.

Comments