Belajar Fullstack Developer - Testing Fullstack
Episode 9 of 28

Belajar Fullstack Developer - Testing Fullstack

Menerapkan piramida testing untuk aplikasi fullstack: unit test untuk logika dan validasi, integration test untuk route handler dan query database, serta end-to-end test dengan Playwright yang menguji alur pengguna dari browser sampai database.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Kode tanpa test itu seperti berjalan di atas es tipis: aman sampai terdengar retakan. Episode ini membahas testing fullstack — bukan "cara menulis test", melainkan strategi test yang benar: apa yang diuji, di lapisan mana, dan berapa banyak. Karena aplikasi fullstack punya banyak lapisan, testing kalian juga harus tersusun berlapis.

Aturan yang akan kita pakai: piramida testing. Banyak test kecil dan cepat di bawah (unit), lebih sedikit test integration di tengah, dan sedikit test end-to-end yang lambat di puncak. Satu test end-to-end yang melindungi alur penting lebih berharga daripada sepuluh unit test yang menguji hal sepele.

Piramida Testing

100%
LapisanContohKecepatanJumlah
UnitValidasi Zod, fungsi murni, komponen UIMilidetikBanyak
IntegrationRoute handler + database, server actionDetikSedang
E2EAlur login → buat catatan → logoutPuluhan detikSedikit

Prinsipnya: test murah untuk hal yang sering berubah, test mahal (E2E) hanya untuk alur kritis yang jarang berubah.

Setup Vitest

Kita pakai Vitest — test runner modern yang terintegrasi dengan Vite (fondasi Next.js). Install bersama Testing Library untuk menguji komponen:

Install tooling test
pnpm add -D vitest @vitejs/plugin-react @testing-library/react @testing-library/jest-dom

Unit Test: Validasi dan Logika Murni

Unit test paling berharga untuk logika murni — dan schema Zod kita dari episode 7 adalah kandidat sempurna:

src/lib/validations.test.ts
import { describe, expect, it } from "vitest"
import { noteSchema } from "./validations"
 
describe("noteSchema", () => {
  it("menerima input valid", () => {
    const result = noteSchema.safeParse({
      title: "Catatan",
      body: "Isi",
      tags: ["kerja"],
    })
    expect(result.success).toBe(true)
  })
 
  it("menolak judul kosong", () => {
    const result = noteSchema.safeParse({ title: "", body: "Isi", tags: [] })
    expect(result.success).toBe(false)
  })
 
  it("menolak lebih dari 5 tag", () => {
    const result = noteSchema.safeParse({
      title: "x",
      body: "y",
      tags: ["1", "2", "3", "4", "5", "6"],
    })
    expect(result.success).toBe(false)
  })
})

Test ini bebas database dan network — jalan dalam milidetik dan memberi kalian kepercayaan penuh bahwa aturan validasi tidak berubah diam-diam.

Tip

Test validasi adalah test dengan ROI tertinggi di aplikasi web: input adalah gerbang semua data, dan aturannya (min/max/format) adalah tempat paling mudah regresi. Mulailah menulis test dari sini jika baru pertama kali.

Integration Test: Route Handler + Database

Integration test memverifikasi lapisan yang lebih tebal: route handler memanggil database nyata. Kita memakai database test terpisah agar tidak mengotori data development:

src/app/api/notes/route.test.ts
import { afterAll, beforeAll, describe, expect, it } from "vitest"
import { prisma } from "@/lib/prisma"
import { POST } from "./route"
 
beforeAll(async () => {
  await prisma.note.deleteMany()
})
 
afterAll(async () => {
  await prisma.note.deleteMany()
})
 
describe("POST /api/notes", () => {
  it("membuat catatan valid dan mengembalikan 201", async () => {
    const req = new Request("http://localhost/api/notes", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ title: "Test", body: "Isi" }),
    })
    const res = await POST(req)
    expect(res.status).toBe(201)
  })
 
  it("mengembalikan 400 untuk title kosong", async () => {
    const req = new Request("http://localhost/api/notes", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ title: "", body: "Isi" }),
    })
    const res = await POST(req)
    expect(res.status).toBe(400)
  })
})

Perhatikan: route handler diuji langsung sebagai fungsi — tanpa menjalankan server. Inilah keindahan integration test di framework fullstack: memanggil fungsi = memanggil endpoint.

End-to-End Test dengan Playwright

Di puncak piramida, Playwright mengendalikan browser sungguhan dan menguji alur dari mata pengguna:

Install Playwright
pnpm create playwright
pnpm exec playwright install chromium
e2e/create-note.spec.ts
import { test, expect } from "@playwright/test"
 
test("pengguna bisa login dan membuat catatan", async ({ page }) => {
  await page.goto("/login")
  await page.getByLabel("Email").fill("test@example.com")
  await page.getByLabel("Password").fill("password-aman")
  await page.getByRole("button", { name: "Masuk" }).click()
 
  await expect(page).toHaveURL("/notes")
 
  await page.goto("/notes/new")
  await page.getByLabel("Judul").fill("Catatan pertama")
  await page.getByLabel("Isi").fill("Dibuat lewat Playwright")
  await page.getByRole("button", { name: "Simpan" }).click()
 
  await expect(page).toHaveURL(/\/notes\/\w+/)
  await expect(page.getByText("Catatan pertama")).toBeVisible()
})

Test ini menguji seluruh tumpukan: UI, server action, validasi, database, redirect. Jika ada satu lapisan yang putus, test ini yang pertama menjerit.

Warning

E2E itu mahal dan rapuh (animasi, timing, browser). Batasi untuk alur kritis: login, checkout, alur data utama. Jangan menjadikan E2E pengganti unit test — itu seperti menguji jembatan hanya dengan meledakkannya sekali.

Praktik: Test End-to-End

Selesaikan dengan urutan berikut:

  1. Tulis unit test untuk schema validasi kalian (mulai dari validations.test.ts).
  2. Tulis integration test untuk satu route handler dengan database test.
  3. Tulis satu E2E: login → buat catatan → catatan muncul di daftar.
  4. Jalankan semua test dan pastikan hijau:
Jalankan semua test
pnpm vitest run
pnpm playwright test

Penutup

Inti yang harus dibawa pulang:

  • Gunakan piramida testing: banyak unit, sedang integration, sedikit E2E.
  • Unit test terbaik untuk validasi dan logika murni — cepat dan murah.
  • Integration test memanggil route handler langsung dengan database test.
  • E2E (Playwright) menguji alur kritis end-to-end dari browser sampai database.
  • Jangan jadikan E2E pengganti unit test; sesuaikan dengan biaya dan manfaat.

Di episode 10 selanjutnya kita membahas styling & design integration — Tailwind, design tokens, dan konsistensi UI — agar aplikasi fullstack kalian tidak hanya bekerja, tapi juga terlihat profesional. Sampai jumpa di episode 10!

Belajar Fullstack Developer - Testing Fullstack | Belajar Fullstack