Episode ini membahas mocking API response dengan route interception, menguji offline mode dan network failures, memvalidasi perilaku backend melalui alur UI, serta integrasi dengan API test tools dan service mocks.

Aplikasi modern hampir selalu bergantung pada API — dan API yang tidak stabil adalah musuh utama test end-to-end. Episode 12 ini membahas dua kemampuan yang saling melengkapi: API testing langsung lewat fixture request, dan network interception untuk mengendalikan respons API di level browser.
Dengan route interception, kalian bisa membuat aplikasi berperilaku seolah server merespons dengan data tertentu, gagal total, atau lambat — semua tanpa menyentuh server sungguhan. Ini membuka skenario yang sebelumnya mustahil diuji dengan andal: offline mode, error handling, loading state, dan kontrak data.
page.route mencegat request yang cocok dengan pola URL sebelum dikirim ke jaringan. Di dalam handler, kalian memutuskan apa yang terjadi: memenuhi request dengan data buatan, melanjutkan ke server, atau membatalkannya.
import { test, expect } from '@playwright/test';
test('daftar produk dari mock', async ({ page }) => {
await page.route('**/api/produk', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, nama: 'Laptop', harga: 15000000 },
{ id: 2, nama: 'Mouse', harga: 250000 },
]),
});
});
await page.goto('/produk');
await expect(page.getByText('Laptop')).toBeVisible();
await expect(page.getByText('Mouse')).toBeVisible();
});route.fulfill(...) mengembalikan respons buatan tanpa benar-benar menghubungi server. Test di atas menguji bagaimana UI merender daftar produk dengan data yang terkontrol sepenuhnya.
Pola URL **/api/produk memakai glob: ** cocok dengan segmen path mana pun. Kalian bisa menargetkan API tertentu secara presisi. Untuk memodifikasi respons server sungguhan — misalnya menyisipkan field — gunakan route.continue():
await page.route('**/api/produk', async (route) => {
const response = await route.fetch();
const data = await response.json();
data.push({ id: 99, nama: 'Produk Tambahan', harga: 1000 });
await route.fulfill({
response,
body: JSON.stringify(data),
});
});route.fetch() mengambil respons asli dari server, lalu kalian mengubah payloadnya sebelum route.fulfill. Pola ini berguna untuk menambahkan data uji tanpa mengubah kontrak server.
Untuk menguji bagaimana aplikasi menangani kegagalan, route.abort membuat request gagal seolah koneksi terputus:
await page.route('**/api/*', (route) => route.abort('failed'));
await page.goto('/beranda');
await expect(page.getByText('Gagal memuat data')).toBeVisible();
await expect(page.getByRole('button', { name: 'Coba Lagi' })).toBeVisible();route.abort('failed') membatalkan semua request API dengan alasan failed. Aplikasi yang baik menampilkan pesan error dan tombol retry — dan inilah yang kalian verifikasi.
Uji bagaimana UI menampilkan state loading sebelum data tiba dengan menunda fulfill:
await page.route('**/api/lambat', async (route) => {
await new Promise((resolve) => setTimeout(resolve, 2000));
await route.fulfill({ status: 200, body: '{}' });
});Menunda respons dengan setTimeout dalam handler memberi kesempatan memverifikasi bahwa spinner atau skeleton muncul sambil menunggu. Pastikan test juga tetap menunggu data akhir, bukan hanya memeriksa loading.
Untuk menguji mode offline total, gunakan context dengan opsi yang menonaktifkan network:
const context = await browser.newContext({
offline: true,
});offline: true membuat seluruh context berjalan tanpa koneksi. Aplikasi yang memakai service worker dan cache akan tetap berfungsi — dan itu yang layak diuji untuk pengalaman offline.
Selain mengendalikan respons, kalian bisa mengamati apa yang dikirim aplikasi ke server:
let requestBody;
await page.route('**/api/checkout', (route) => {
requestBody = route.request().postDataJSON();
route.continue();
});
await page.getByRole('button', { name: 'Bayar' }).click();
expect(requestBody).toMatchObject({
email: 'user@example.com',
total: 15000000,
});route.request().postDataJSON() mengembalikan body request sebagai objek. Dengan ini, validasi perilaku backend dilakukan lewat UI — memastikan bahwa klik kalian benar-benar mengirim data yang tepat ke server.
Pendekatan yang seimbang: uji kontrak API langsung dengan fixture request, lalu uji bahwa UI memakai API tersebut dengan benar lewat route. Jika API berubah, test API langsung memberi sinyal cepat tanpa browser; jika UI salah mengirim data, interception test menangkapnya.
Playwright memiliki fixture request untuk HTTP test tanpa browser — cepat dan cocok untuk smoke test API:
import { test, expect } from '@playwright/test';
test('health check API', async ({ request }) => {
const response = await request.get('/api/health');
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body.status).toBe('up');
});request.get('/api/health') mengirim HTTP GET langsung. Kombinasikan dengan baseURL di konfigurasi agar path relatif berfungsi. Test semacam ini murah dan bisa dijalankan sangat cepat.
Ketika data uji terlalu kompleks untuk ditulis inline, pisahkan ke file JSON dan penuhi via route dengan data tersebut:
import dataProduk from '../fixtures/produk.json';
await page.route('**/api/produk', (route) => {
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(dataProduk) });
});dataProduk dimuat dari file fixtures — dataset yang sama bisa dipakai di banyak test dan mudah diperbarui. Ini menjaga konsistensi antara data mock dan harapan assertion.
npx playwright test tests/api.spec.tsEpisode 12 memberikan kalian kendali penuh atas lapisan jaringan: mocking dan modifikasi respons API lewat page.route, simulasi offline dan network failures untuk menguji error handling, validasi payload yang dikirim aplikasi, serta integrasi dengan fixture request untuk API test yang cepat dan service mocks yang terpusat.
Inti yang harus dibawa pulang:
route.fulfill mengendalikan respons API tanpa menyentuh server.route.abort('failed') mensimulasikan kegagalan jaringan.route.request().postDataJSON memvalidasi payload yang dikirim aplikasi.request memberi API test cepat tanpa browser.Di episode 13 selanjutnya kita akan membahas security dan isolation — menjalankan test dalam browser contexts yang terisolasi, menangani secrets dan environment variables dengan aman, melindungi test data dan credentials, serta mencegah state leakage antar test.