Lompat ke konten utama

Unit Testing React: Panduan Praktis End-to-End

Kuasai unit testing React dari setup hingga CI/CD. Panduan ini membahas Jest, RTL, hooks, async code, mocking, dan best practices untuk aplikasi cross-platform yang tangguh.

Unit Testing React: Panduan Praktis End-to-End

Anda melakukan push perubahan UI kecil sebelum makan siang. Terlihat aman. Label tombol berubah, conditional render disederhanakan, dan helper hook menangani satu branch baru. Pull request bersih, review cepat, dan deploy pun berjalan.

Satu jam kemudian, tim support melaporkan login tidak berfungsi di satu platform. Web terlihat normal. Desktop shell memiliki render path yang usang. Mobile build berperilaku berbeda setelah async state change. Tidak ada yang menyadarinya karena code punya test, tapi bukan test yang tepat, dan pastinya tidak punya sistem yang reliabel di sekitar test tersebut.

Itulah masalah utama unit testing React di tim produksi. Menulis beberapa test yang passing itu tidak sulit. Membangun suite yang tetap melindungi Anda saat refactor, release trains, hotfix, dan cross-platform packaging adalah bagian sulitnya. Aplikasi React tidak gagal karena tim lupa cara memanggil render(). Aplikasi gagal karena test terlalu fokus pada detail implementasi, perilaku async hanya ditutupi saja, dan CI menganggap testing sekadar checklist, bukan sebagai release gate.

Unit testing React modern bekerja layaknya sistem keamanan. Feedback cepat secara lokal. Pengecekan deterministik di CI. Batasan yang jelas antara apa yang masuk ke unit test dan apa yang tidak. Hal ini semakin krusial saat codebase React yang sama dijalankan di browser, kontainer Capacitor, atau shell Electron.

Table of Contents

Mengapa unit testing React adalah safety net terbaik Anda

Unit test sangat berguna saat berhasil menangkap kesalahan yang Anda pikir tidak mungkin terjadi. Di React, hal ini biasanya berarti komponen tetap render, tetapi perilaku yang diandalkan user sudah berubah. Tombol yang seharusnya disabled jadi bisa diklik. Loading state tidak kunjung hilang. Pesan fallback hilang setelah refactor. Kegagalan tersebut kecil di code tapi mahal di production.

Testing React berubah secara signifikan ketika React Testing Library menjadi standar utama untuk testing behavior, bukan internals, mendorong tim ke arah test yang mencerminkan perilaku user daripada props atau state komponen, seperti yang tercermin dalam panduan testing React Native di Overview testing React Native. Perubahan ini penting karena React code terus berubah. Hooks berpindah. Komponen di-split. Context ditambahkan. Test yang terikat pada struktur internal akan break saat refactor yang sehat. Test yang terikat pada behavior yang terlihat biasanya tetap bertahan.

Apa yang seharusnya dilindungi unit test

Unit test React yang baik melindungi satu kontrak kecil:

  • Output render: Apakah user melihat teks, label, state, atau fallback yang benar?
  • Perilaku interaksi: Apakah klik, ketikan, atau toggle mengubah UI dengan benar?
  • Penanganan boundary: Apakah komponen berperilaku dengan benar saat menerima input yang diharapkan, data yang hilang, atau jalur error?

Test yang lemah menguji hal yang salah:

  • Internal komponen: Bentuk state, private methods, atau props khusus implementasi
  • Mekanisme framework: Apakah React memperbarui hook secara internal sesuai dengan ekspektasi Anda
  • Detail child: Markup milik komponen bersarang yang tidak perlu Anda verifikasi di sini

Aturan praktis: Jika Anda bisa melakukan refactor komponen tanpa mengubah tampilan atau fungsi bagi user, test tersebut juga tidak perlu berubah.

Unit test juga merupakan bagian dari sistem testing yang lebih luas. Tujuannya bukan untuk membuktikan seluruh aplikasi berjalan end-to-end. Unit test adalah layer cepat untuk menangkap regresi sebelum Anda butuh browser-level test atau validasi di level device. Itulah mengapa unit test menjadi lini pertahanan pertama dalam stack automated testing untuk aplikasi production.

Bagi tim React yang sering melakukan deployment, kepercayaan diri muncul dari pembagian kerja ini. Unit test menangkap regresi lokal dengan cepat. Integration test memverifikasi koneksi antar modul. End-to-end test mengonfirmasi jalur kritis. Jika melewatkan layer unit, semua proses yang lebih lambat di downstream akan terbebani terlalu berat.

Menyiapkan Lingkungan Testing React Modern Anda

Lingkungan test yang rapuh akan menciptakan flaky tests bahkan sebelum Anda menulis satu assertion pun. Banyak developer menyalahkan Jest, jsdom, atau React, padahal masalah utamanya adalah konfigurasi yang tidak konsisten antara mesin lokal dan CI. Solusinya adalah membuat environment yang 'boring'. Dalam hal ini, boring itu bagus.

Workspace bersih yang menampilkan monitor komputer dengan code React unit testing di dalam editor code.

Mulai dengan runner dan environment yang predictable

Untuk aplikasi React modern, terutama yang dibuat dengan Vite, setup dasarnya harus mencakup:

  • Test runner: Jest masih umum digunakan, terutama pada codebase React lama dan stack CI enterprise.
  • Lingkungan mirip browser: jsdom memungkinkan test komponen merender output DOM.
  • Utilitas Testing Library: @testing-library/react dan @testing-library/jest-dom
  • Satu entrypoint setup: Satu file untuk mendaftarkan matcher dan mock global

Workflow utama yang ditekankan panduan testing React sangat simpel: render komponen di lingkungan berbasis jsdom, query UI dengan selector seperti getByText atau getByRole, picu interaksi, lalu assert perubahan DOM, seperti yang dijelaskan dalam dokumentasi testing React. Workflow tersebut hanya bisa dipercaya jika setiap mesin menjalankan test environment yang sama.

Setup Jest yang praktis biasanya seperti ini:

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapper: {
    '\\.(css|less|scss)$': 'identity-obj-proxy',
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
  },
};

Jika tim Anda menggunakan SWC alih-alih Babel, tidak masalah. Intinya bukan pada transformernya, melainkan konsistensi. Pilih satu jalur dan standarisasi di dalam repo. Jika Anda butuh referensi pendamping untuk konvensi testing JavaScript yang lebih luas, Capgo punya panduan unit tests JavaScript yang berguna sebagai dokumen handoff tim.

Tambahkan file setup yang akan digunakan suite Anda

Setup yang tepat setupTests.js mengurangi banyak noise yang berulang:

import '@testing-library/jest-dom';

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

Di file inilah Anda menyelesaikan celah environment satu kali saja, alih-alih di dua puluh file test berbeda. Tambahkan mock untuk API yang dibutuhkan UI Anda, seperti matchMedia, ResizeObserver, atau IntersectionObserver, jika library komponen Anda membutuhkannya.

Tanpa ini, developer akan melakukan patch globals secara ad hoc. Hal ini menyebabkan test tidak konsisten dan kegagalan yang sulit dilacak. Run lokal satu orang berhasil karena mereka menambah manual mock di sebuah file, tapi CI gagal karena setup-nya tidak dibagikan.

Jaga agar perilaku lokal dan CI tetap selaras

Perintah lokal harus semirip mungkin dengan perintah CI. Jika developer menjalankan watch mode dengan default yang longgar sementara CI menggunakan konfigurasi yang lebih ketat, Anda akan menghadapi kegagalan tak terduga setelah merge. Pastikan script tetap eksplisit:

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:ci": "jest --runInBand --coverage"
  }
}

Panduan singkat membantu anggota tim baru mencapai baseline yang sama dengan cepat:

Pilihan setup yang paling berdampak adalah disiplin terhadap nilai default. Masukkan alias ke dalam config. Letakkan environment mocks dalam satu file setup. Gunakan jsdom untuk UI tests dan environment yang lebih ringan untuk pure utilities jika memungkinkan. Semakin sedikit custom behavior yang dibutuhkan setiap test, semakin andal sistem Anda.

Menulis Component Tests yang Bermakna

Masalah organisasi bukanlah kesulitan menulis tes, melainkan kesulitan menulis tes yang tetap relevan enam bulan kemudian.

Pola standar untuk unit testing komponen React masih merupakan cara yang paling tepat: render komponen, query UI dengan selector yang berpusat pada user, trigger interaksi, dan assert perubahan DOM yang dihasilkan, sehingga test tetap terpisah dari implementation details seperti state atau props, sebagaimana dijelaskan dalam panduan testing React. Kuncinya adalah menerapkan pola tersebut dengan bijak.

Test accordion seperti cara user menggunakannya

Ambil sebuah Accordion komponen dasar. Komponen ini merender tombol dengan judul. Konten panel awalnya tersembunyi. Mengklik tombol akan menampilkan konten dan memperbarui state aksesibilitas.

Perilaku itu sudah cukup untuk beberapa test yang berguna:

  1. Render awal menampilkan judul tapi tidak kontennya.
  2. Mengklik trigger akan menampilkan konten.
  3. Mengklik lagi akan menyembunyikannya.
  4. Atribut aksesibilitas mencerminkan state yang terlihat.

Poin terakhir itu sering terlewat. Jika komponen Anda menggunakan aria-expanded, aria-controls, atau struktur berbasis role, verifikasi semuanya. Itu bukan detail implementasi. Itu bagian dari kontrak yang dilihat pengguna.

Test komponen terbaik terbaca seperti laporan bug yang tidak ingin Anda terima.

Pilih query berdasarkan intent

React Testing Library menyediakan beberapa gaya query, tapi tidak bisa saling menggantikan. Memilih yang salah akan membuat test berisik atau menyesatkan.

Tipe Query Saat Elemen Ditemukan Saat Elemen Tidak Ditemukan Contoh Use Case
getBy Mengembalikan elemen secara instan Melempar error secara instan Memastikan tombol atau heading sudah ada di layar
queryBy Mengembalikan elemen secara instan Mengembalikan null Memastikan konten tersembunyi belum ada sebelum interaksi
findBy Resolved saat elemen muncul Rejected setelah menunggu Assert konten yang dimuat async muncul setelah fetch atau update tertunda

Model mental sederhana bisa membantu:

  • Gunakan getBy untuk hal-hal yang sudah harus ada.
  • Gunakan queryBy untuk hal-hal yang belum boleh ada.
  • Gunakan findBy saat UI berubah nanti.

Jika sebuah test dimulai dengan findBy untuk segalanya, biasanya penulisnya tidak yakin kapan komponen update. Ketidakpastian itu akan jadi flakiness nantinya.

Contoh praktis accordion

Berikut komponen representatifnya:

function Accordion({ title, children }) {
  const [open, setOpen] = React.useState(false);

  return (
    <section>
      <button
        aria-expanded={open}
        aria-controls="accordion-panel"
        onClick={() => setOpen(prev => !prev)}
      >
        {title}
      </button>
      {open ? (
        <div id="accordion-panel">
          {children}
        </div>
      ) : null}
    </section>
  );
}

Berikut adalah kriteria test yang layak dipertahankan:

import { render, screen, fireEvent } from '@testing-library/react';

test('renders the accordion title and hides content initially', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  expect(screen.getByRole('button', { name: /shipping details/i })).toBeInTheDocument();
  expect(screen.queryByText(/delivery takes 3 days/i)).not.toBeInTheDocument();
});

test('reveals content when the trigger is clicked', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  fireEvent.click(screen.getByRole('button', { name: /shipping details/i }));

  expect(screen.getByText(/delivery takes 3 days/i)).toBeInTheDocument();
});

test('updates aria-expanded when opened', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  const button = screen.getByRole('button', { name: /shipping details/i });
  expect(button).toHaveAttribute('aria-expanded', 'false');

  fireEvent.click(button);

  expect(button).toHaveAttribute('aria-expanded', 'true');
});

Apa yang hilang juga sama pentingnya. Tidak ada assertion terhadap internal state. Tidak ada pengecekan apakah setOpen telah dipanggil. Tidak ada snapshot dari seluruh rendered tree. Test tersebut justru menambah beban maintenance, bukan kepercayaan diri.

Beberapa kebiasaan ini membuat component test lebih kuat:

  • Utamakan role-based queries: Button, heading, dialog, alert, dan input biasanya harus ditemukan melalui role.
  • Buat setiap test tetap spesifik: Satu user-visible behavior per test membuat kegagalan lebih mudah dibaca.
  • Beri nama test berdasarkan outcome: “updates aria-expanded when opened” jauh lebih berguna daripada “works correctly.”

Jika sebuah component sulit di-test melalui DOM, itu sering kali menunjukkan adanya masalah desain. Mungkin state disembunyikan di tempat yang salah. Mungkin kurang semantic markup. Test yang baik sering kali mendorong tim untuk membuat component yang lebih baik.

Testing Custom Hooks dan Application Logic

Aplikasi React menyembunyikan banyak perilaku penting di luar komponen. Transisi state ada di dalam hooks. Validasi dan formatting ada di fungsi helper. Pengolahan data sering terjadi sebelum rendering. Jika Anda hanya mengetes komponen yang terlihat, Anda akan melewatkan banyak bagian code yang tetap bisa merusak perilaku produksi.

Hooks butuh harness yang sadar React

Custom hook tetap butuh React agar bisa berjalan dengan benar, jadi teslah menggunakan renderHook dan bungkus pemanggilan pengubah state di dalam act().

Sebuah useToggle hook kecil adalah contoh yang bagus:

import { useState, useCallback } from 'react';

export function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = useCallback(() => setValue(current => !current), []);
  return { value, toggle };
}

Tesnya harus tetap fokus pada public contract:

import { renderHook, act } from '@testing-library/react';
import { useToggle } from './useToggle';

test('returns the initial value', () => {
  const { result } = renderHook(() => useToggle(true));
  expect(result.current.value).toBe(true);
});

test('toggles the value', () => {
  const { result } = renderHook(() => useToggle(false));

  act(() => {
    result.current.toggle();
  });

  expect(result.current.value).toBe(true);
});

Tes tersebut berguna karena hook itu sendiri adalah unitnya. Anda tidak sedang mengetes internal React. Anda sedang memverifikasi perilaku eksternal dari hook tersebut.

Bagi tim produk yang membangun UI reusable atau primitive fitur, pola ini sangat penting. Hooks sering menjadi interface yang digunakan di berbagai aplikasi, design system, atau internal tooling. Jika Anda merancang perilaku reusable untuk tujuan komersial, sumber daya tentang hooks untuk produk maker dapat membantu memandang hooks sebagai building block produk, bukan sekadar detail implementasi.

Logika murni harus tetap murni dalam tes

Tidak semua hal butuh jsdom, React, atau Testing Library. Jika sebuah fungsi bersifat pure, tes saja dengan Jest biasa di lingkungan Node.

Contoh:

export function formatDisplayName(firstName: string, lastName: string) {
  return `${firstName.trim()} ${lastName.trim()}`.trim();
}

Tes tersebut harusnya sangat simpel:

import { formatDisplayName } from './formatDisplayName';

test('joins and trims both names', () => {
  expect(formatDisplayName(' Ada ', ' Lovelace ')).toBe('Ada Lovelace');
});

test('handles a missing last name', () => {
  expect(formatDisplayName('Ada', '')).toBe('Ada');
});

Keuntungannya adalah kecepatan dan kejelasan. Jika sebuah fungsi tidak butuh rendered tree, jangan berikan itu. Tooling khusus React hanya menambah overhead. Jaga tes business logic tetap kecil, cepat, dan dekat dengan fungsi yang diverifikasi.

Pembagian praktis yang efektif:

  • Hooks: Gunakan renderHook, act(), dan wrapper provider jika diperlukan.
  • Utilities: Gunakan Jest biasa tanpa DOM.
  • Logika stateful cross-cutting: Pindahkan ke helper yang bisa di-test saat test komponen mulai terlalu kompleks.

Tim sering kali membebani test komponen dengan assertion logika yang seharusnya ada di layer bawah. Memisahkan logika tersebut memberi dua keuntungan: test komponen jadi lebih bersih, dan test logika jadi lebih cepat.

Menguasai Teknik Lanjutan: Mocking dan Async

Kebanyakan suite React yang tidak reliabel rusak di dua tempat: pada boundary dependency, dan pada masalah waktu.

Itulah mengapa async testing dan mocking menjadi pembeda antara suite test mainan dengan suite yang bisa Anda percayai sebelum rilis. Sebuah analisis menyebutkan 46,5% flakiness test disebabkan oleh masalah lingkungan atau resource seperti timing async di analisis unit testing React ini. Pada aplikasi React, hal ini berkaitan langsung dengan transisi state, rendering yang tertunda, UI berbasis network, dan test yang hanya menebak-nebak alih-alih menunggu secara deterministik.

Tabel perbandingan Teknik Advanced React Testing, fokus pada Mocking Dependencies vs Asynchronous Testing.

Mock pada boundary-nya, bukan setiap layer

Cara tercepat membuat test yang menyesatkan adalah dengan me-mock setengah component tree Anda, lalu melakukan assertion bahwa mock buatan Anda sendiri berhasil.

Untuk komponen yang mengambil data akun, mock network client atau modul API. Jangan mock hook, komponen child row, loading spinner, atau tiga fungsi utility kecuali test tersebut memang butuh isolasi di bagian tersebut.

Gunakan aturan ini:

  • Mock layanan eksternal: HTTP client, analytics, browser-only API, native bridge
  • Mock API platform yang tidak stabil: matchMedia, timer, interface preload Electron, plugin Capacitor saat tidak tersedia di jsdom
  • Hindari mock internal sendiri secara default: custom hook, child sederhana, utility lokal

Jika test lulus hanya karena bagian sulit diganti dengan fake, itu tidak memberikan rasa percaya diri saat release.

Untuk tim yang butuh contoh dan pola seputar runner API, Capgo tutorial testing adalah library referensi praktis, terutama saat onboarding developer yang paham React tapi belum paham mekanika testing.

Test async gagal karena timing tidak jelas

Kegagalan async biasanya disebabkan oleh salah satu dari tiga kesalahan ini:

  1. Test melakukan assert terlalu dini.
  2. Test menunggu menggunakan timer sembarangan.
  3. Komponen update lebih dari sekali, tapi test hanya memodelkan satu transisi.

Test async yang stabil biasanya memiliki pola seperti ini:

test('shows user details after data loads', async () => {
  render(<UserProfile userId="42" />);
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  expect(await screen.findByText(/account owner/i)).toBeInTheDocument();
});

Atau, saat Anda perlu menunggu kondisi spesifik:

await waitFor(() => {
  expect(screen.getByRole('alert')).toBeInTheDocument();
});

Gunakan findBy saat munculnya satu elemen adalah event yang Anda cari. Gunakan waitFor saat kondisinya lebih luas atau state tidak bisa dinyatakan dengan satu query. Hindari setTimeout dalam test kecuali Anda memang sedang mengetes perilaku timer dan menggunakan fake timers.

Ekosistem testing React juga mengharuskan Anda untuk menghormati act() semantik seputar update. Testing Library menangani banyak hal ini, tetapi jika Anda mengelola state secara manual atau memajukan timer, Anda tetap perlu memikirkan kapan update tersebut flush.

Pahami tool mocking mana yang harus digunakan

Tool mocking yang berbeda menyelesaikan masalah yang berbeda:

Tool Kegunaan terbaik Kesalahan umum
jest.fn() Fake callback mandiri atau fungsi yang di-inject Menggunakannya untuk mengganti seluruh module padahal cukup dengan callback sederhana
jest.spyOn() Observe atau override satu method pada object atau module asli Lupa mengembalikan (restore) implementasi asli
jest.mock() Ganti dependency module pada batas import Mocking module besar secara default sehingga kehilangan behavior yang bermakna

Contoh membantu:

  • Gunakan untuk jest.fn() saat komponen menerima onSubmit prop.
  • Gunakan jest.spyOn() saat Anda perlu memverifikasi console.error, metode penyimpanan, atau pemanggilan API yang diekspor.
  • Gunakan jest.mock() saat mengimpor modul berisiko memicu I/O, code native, atau perilaku di luar batas unit.

Satu area tingkat lanjut yang jarang dibahas panduan lain adalah pengujian error-path di React modern. Error boundaries, perubahan state yang tertunda, dan UI fallback async layak mendapatkan test kelas satu, bukan sekadar contoh klik “happy path”. Jika child component melempar error, pastikan UI fallback muncul. Jika request gagal, pastikan state pemulihan terlihat. Jika tombol disable saat loading, pastikan juga hal itu. Itulah bug yang paling diingat pengguna.

Meningkatkan Kualitas dan Strategi Test

Banyak tim masih mengejar coverage seolah-olah itu sama dengan kepercayaan diri. Padahal tidak.

Anda bisa mencapai target coverage tapi tetap melewatkan regresi yang krusial. Suite yang penuh dengan shallow assertions, snapshot yang terlalu luas, dan mocked internals hanya memberi kesan aman namun justru meningkatkan biaya maintenance.

Infografis yang membandingkan manfaat pengujian berkualitas versus beban maintenance dari suite pengujian berjumlah besar.

Coverage adalah peta, bukan tujuan akhir

Laporan coverage berguna jika bisa menjawab satu pertanyaan: jalur kritis mana yang belum terlindungi?

Laporan ini tidak berguna jika memaksa developer menguji wrapper trivial, markup statis, atau file pass-through satu baris hanya demi menaikkan persentase. Gunakan coverage sebagai alat discovery. Jika state autentikasi, aksi billing, feature flags, atau update prompts belum ada tesnya, itu adalah sinyal. Jika komponen icon presentasional belum ada tesnya, biasanya itu bukan masalah.

Pertanyaan review yang sehat itu sederhana: apakah tes ini mengurangi risiko rilis?

  • Ya: Tes ini memverifikasi perilaku yang terlihat oleh user pada jalur kritis.
  • Mungkin: Tes ini melindungi business logic yang mudah rusak saat refactor.
  • Tidak: Hal ini memverifikasi detail implementasi atau menduplikasi nilai dari pengujian lain.

Apa yang tidak perlu di-unit test

Banyak panduan React yang belum membahas hal-hal yang sebaiknya dilewati. Celah ini penting karena over-mocking dan testing detail implementasi membuat test suite menjadi rapuh; test bisa saja pass, padahal user experience tetap rusak, seperti yang disebutkan dalam panduan BrowserStack tentang apa yang tidak perlu di-unit test di React.

Lewati atau batasi pola berikut:

  • Assertion pada internal state: Jangan test isOpen secara langsung jika Anda bisa mengetes apakah panelnya sudah terbuka.
  • Perilaku framework: Jangan test apakah React memanggil sebuah effect. Test hasil dari perubahan yang dilakukan effect tersebut.
  • Internal library pihak ketiga: Test integrasi Anda dengan date picker atau router, bukan logika rendering dari library itu sendiri.
  • Unit yang terlalu dipecah: Jika Anda melakukan mock pada setiap child dan helper, Anda mungkin tidak lagi menguji perilaku yang bermakna.

Test yang buruk lebih berbahaya daripada tidak ada test sama sekali jika mereka menghambat refactor tapi tetap gagal mendeteksi bug di produksi.

Heuristik yang berguna adalah kepemilikan boundary. Uji apa yang dimiliki oleh code Anda. Jangan menguji apa yang sudah dikelola React, browser, atau library matang lainnya, kecuali layer integrasi Anda mengubah kontraknya.

Kapan snapshot membantu dan kapan mereka merugikan

Snapshot tidaklah sia-sia. Hanya saja, snapshot sangat mudah disalahgunakan.

Gunakan secara hemat untuk komponen dengan output stabil dan sederhana di mana perbedaan struktur yang luas itu bermakna. Hindari untuk komponen interaktif atau sangat dinamis karena akan menjadi noise. Developer akan berhenti membacanya dan mulai melakukan update secara refleks.

Biasanya ada alternatif yang lebih baik:

  • Untuk conditional rendering, pastikan ada atau tidaknya teks kunci.
  • Untuk perubahan state visual, pastikan role, label, atau atribut yang relevan.
  • Untuk error dan fallback, pastikan pesan aktual atau area alert-nya.

Jika tim Anda membutuhkan proses kualitas yang lebih luas di luar unit test, pendamping yang solid adalah workflow app quality assurance yang memperlakukan test, pengecekan rilis, dan perencanaan rollback sebagai satu sistem. Itulah perubahan mindset yang paling cepat meningkatkan kualitas test. Jangan lagi tanya berapa banyak test yang Anda punya. Mulailah bertanya kegagalan mana yang masih bisa sampai ke user.

Mengintegrasikan Test ke dalam Pipeline CI/CD Cross-Platform

Test suite yang hanya berjalan di laptop developer hanyalah sebuah saran, bukan sebuah kontrol.

Suite tersebut menjadi operasional saat setiap pull request menjalankan pengecekan yang sama di environment yang bersih dan memblokir merge jika pengecekan tersebut gagal. Kedengarannya sepele, tapi banyak tim masih menyisakan celah kritis. Test dijalankan secara manual. Laporan coverage bersifat opsional. Job packaging dan release dimulai sebelum job test selesai. Itulah cara regresi UI kecil bisa lolos menjadi kegagalan rilis yang besar.

Flowchart lima langkah yang mengilustrasikan proses integrasi automated test React ke dalam pipeline pengembangan CI/CD.

Pull request harus memicu gate yang sama setiap kali

Agar unit testing React berfungsi sebagai safety net, CI membutuhkan beberapa hal esensial:

  • Jalankan pada setiap pull request
  • Install dependencies dari lockfile
  • Gunakan command test yang sama setiap saat
  • Fail fast saat terjadi kegagalan test
  • Publish artifacts hanya setelah test berhasil

Ini adalah inti dari praktik continuous deployment bagi tim aplikasi. Bangun kepercayaan sebelum rilis, bukan setelahnya.

Workflow GitHub Actions yang simpel sudah cukup bagi banyak tim:

name: test

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  react-tests:
    runs-on: ubuntu-latest

    steps:
      - name: Check out code
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm run test:ci

Ini tidak rumit, dan itulah poinnya. Pipeline terkuat biasanya adalah yang paling minim kejutan.

Mengapa ini lebih penting untuk Capacitor dan Electron

Aplikasi React cross-platform memiliki risiko rilis lebih tinggi dibanding aplikasi browser-only karena code UI yang sama sering kali dikirim dalam container berbeda dengan asumsi runtime yang berbeda.

Beberapa contoh menunjukkan di mana pipeline sangat membantu:

  • Aplikasi Capacitor: code web mungkin lolos saat testing lokal, tapi gagal ketika plugin bridge, kondisi offline, atau edge case lifecycle aplikasi mengubah perilaku setelah packaging.
  • Aplikasi Electron: Komponen renderer mungkin bergantung pada preload API, window messaging, atau state khusus desktop yang tidak akan ada dalam testing browser biasa kecuali jika di-mock secara sengaja.
  • Release train bersama: Satu bundle bermasalah bisa memengaruhi banyak target jika proses deployment Anda tidak membatasi publikasi secara ketat.

Itulah sebabnya unit test harus dijalankan sebelum job packaging, dan job packaging harus berjalan sebelum job distribution. Setiap tahap mempersempit risiko. Unit test menangkap regresi lokal dengan cepat. Packaging per platform memverifikasi asumsi environment. Persetujuan manual atau staged rollout menangani keyakinan akhir pada rilis.

Workflow GitHub Actions yang praktis

Pipeline yang lebih matang biasanya membagi tanggung jawab:

  1. Job test: Unit test dan hook test cepat
  2. Job build: Production build hanya setelah test lulus
  3. Job package: Capacitor sync, Electron packaging, atau artifact bundling
  4. Job release: Publish hanya dari branch atau tag yang disetujui

Bagi tim yang mengirimkan live updates ke Capacitor atau aplikasi Electron, di sinilah peran release tooling menjadi krusial. Salah satu opsi dalam workflow tersebut adalah Capgo, yang mempublikasikan signed web bundles untuk aplikasi CapacitorJS dan Electron dengan dukungan rollback dan kontrol rollout berbasis channel. Praktiknya, ini berarti job test React Anda bisa menjadi gate pertama sebelum web bundle dipromosikan ke staging atau production.

Aturan operasionalnya sederhana. Jangan biarkan infrastruktur release menutupi kelemahan test. Gunakan infrastruktur release setelah test yang andal berhasil menyaring perubahan yang buruk.

Sistem testing yang andal mengubah perilaku tim. Engineer melakukan merge dengan lebih percaya diri. Reviewer fokus pada edge cases daripada menjalankan hal dasar secara manual. Release manager tidak lagi menganggap setiap deploy sebagai perjudian. Itulah hasil dari unit testing React yang baik.


Jika tim Anda mengirimkan React melalui Capacitor atau Electron, keamanan release bergantung pada lebih dari sekadar local tests yang lolos. Capgo memberikan cara terkontrol bagi tim untuk mempublikasikan signed web updates, menargetkan channel rollout, dan melakukan rollback pada bundle yang bermasalah tanpa menunggu review store, yang sangat cocok diintegrasikan dengan CI pipeline yang mewajibkan unit tests lolos sebelum deployment.

Live update untuk aplikasi Capacitor

Saat bug di web layer sudah live, kirim perbaikannya lewat Capgo alih-alih menunggu berhari-hari untuk persetujuan app store. Pengguna menerima update di background, sementara perubahan native tetap di jalur review normal.

Terbaru dari Blog kami

Capgo memberikan insight terbaik yang Anda butuhkan untuk membuat aplikasi mobile yang benar-benar profesional.