Langsung 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 practice untuk aplikasi cross-platform yang tangguh.

Ilustrasi blog Capgo untuk 'Unit Testing React: Panduan End-to-End Praktis'

Anda push perubahan UI kecil sebelum makan siang. Tampaknya tidak berbahaya. Label tombol berubah, conditional render disederhanakan, dan helper hook mendapat satu cabang baru. Pull request bersih, review cepat, dan deploy berhasil.

Satu jam kemudian, support melaporkan bahwa login berhenti berfungsi di satu platform. Web terlihat normal. Desktop shell memiliki jalur render yang usang. Mobile build berperilaku berbeda setelah perubahan state asinkron. Tidak ada yang menangkapnya karena code memiliki tes, tetapi bukan tes yang tepat, dan pasti bukan sistem yang andal di sekitar tes tersebut.

Itulah masalah utama unit testing React di tim produksi. Menulis beberapa tes yang lulus tidak sulit. Membangun suite yang tetap melindungi Anda selama refactoring, release train, hotfix, dan packaging lintas platform adalah bagian yang sulit. Aplikasi React tidak gagal karena tim lupa cara memanggil render(). Mereka gagal karena tes bergeser ke detail implementasi, perilaku asinkron diabaikan, dan CI memperlakukan pengujian seperti kotak centang alih-alih gerbang rilis.

Unit testing React modern berfungsi ketika berperilaku seperti sistem keselamatan. Umpan balik cepat secara lokal. Pengecekan deterministik di CI. Batasan yang jelas tentang apa yang termasuk dalam unit test dan apa yang tidak. Itu semakin penting ketika codebase React yang sama dikirim melalui browser, kontainer Capacitor, atau shell Electron.

Table of Contents

Mengapa Unit Testing React Jadi Safety Net Terbaik Anda

Unit test membuktikan manfaatnya saat menangkap kesalahan yang Anda yakin tidak mungkin terjadi. Di React, itu biasanya berarti komponen tetap dirender, tetapi perilaku yang diandalkan pengguna sudah berubah. Tombol yang dinonaktifkan jadi bisa diklik. Status loading tidak pernah hilang. Pesan fallback menghilang setelah refactor. Kegagalan seperti itu kecil di code tapi mahal di produksi.

Pengujian React berubah secara signifikan ketika React Testing Library menjadi model utama untuk menguji perilaku, bukan internal, mendorong tim untuk menulis test yang mencerminkan perilaku pengguna, bukan props atau state komponen, seperti tercermin dalam panduan pengujian React Native di Ringkasan pengujian React Native. Pergeseran ini penting karena React code sering dirombak. Hooks berpindah. Komponen dipecah. Context diperkenalkan. Test yang terikat pada struktur internal akan rusak saat refactor yang sehat. Test yang terikat pada perilaku yang terlihat biasanya bertahan.

Apa yang harus dilindungi unit test

Unit test React yang baik melindungi satu kontrak kecil:

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

Tes yang lemah melindungi hal yang salah:

  • Internal komponen: Bentuk state, metode privat, props yang hanya untuk implementasi
  • Mekanika framework: Apakah React memperbarui hook dengan cara internal yang persis Anda harapkan
  • Detail child: Markup yang dimiliki oleh komponen bersarang yang tidak Anda maksud untuk diverifikasi di sini

Aturan praktis: Jika Anda bisa refaktor komponen tanpa mengubah apa yang dilihat atau dilakukan pengguna, tes juga tidak perlu berubah.

Unit test juga berada dalam sistem pengujian yang lebih luas. Tujuannya bukan membuktikan seluruh aplikasi bekerja end to end. Ini adalah lapisan cepat yang menangkap regresi sebelum Anda membutuhkan tes level browser atau validasi level perangkat. Itulah mengapa ini menjadi garis pertahanan pertama dalam stack yang masuk akal dari pengujian otomatis untuk aplikasi produksi.

Bagi tim React yang sering rilis, kepercayaan diri datang dari pembagian tugas ini. Unit test menangkap regresi lokal dengan cepat. Integration test memverifikasi sambungan. End-to-end test memastikan jalur kritis. Lewati lapisan unit, dan semua hal yang lebih lambat di hilir harus menanggung beban terlalu besar.

Menyiapkan lingkungan testing React modern Anda

Lingkungan pengujian yang rapuh menciptakan flaky test sebelum Anda menulis satu pun assertion. Banyak developer menyalahkan Jest, jsdom, atau React ketika masalah dasarnya adalah konfigurasi yang tidak konsisten di antara mesin lokal dan CI. Solusinya adalah membuat lingkungan menjadi membosankan. Membosankan itu bagus di sini.

Ruang kerja bersih dengan monitor komputer yang menampilkan pengujian unit React code di editor code.

Mulai dengan runner dan environment yang prediktabel

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

  • Sebuah 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 global mocks

Workflow utama yang ditekankan panduan testing React itu simpel: render komponen di lingkungan berbasis jsdom, query UI dengan selector seperti getByText atau getByRole, trigger interaksi, lalu assert perubahan DOM, seperti yang dijelaskan dalam dokumentasi testing React. Workflow tersebut hanya akan tetap terpercaya 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 transformernya, tapi konsistensi. Pilih satu jalur dan standarkan di repo. Jika Anda ingin referensi pendamping yang bagus untuk konvensi testing JavaScript yang lebih luas, Capgo panduan unit tests JavaScript ini adalah dokumen handoff tim yang berguna.

Tambahkan file setup yang dibutuhkan suite Anda

Setup yang tepat setupTests.js mengurangi banyak noise 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(),
  })),
});

File ini adalah tempat Anda menyelesaikan gap environment satu kali saja, bukan di dalam dua puluh file test. Tambahkan mock untuk API yang dibutuhkan UI Anda, seperti matchMedia, ResizeObserver, atau IntersectionObserver, jika library komponen Anda membutuhkannya.

Tanpa ini, developer akan melakukan patch pada globals secara ad hoc. Hal ini menyebabkan test tidak konsisten dan kegagalan yang sulit dilacak. Run lokal seseorang lolos karena ia menambahkan mock manual di sebuah file, sementara CI gagal karena setup-nya tidak dibagikan.

Samakan perilaku lokal dengan CI

Perintah lokal harus sedekat mungkin dengan perintah CI. Jika developer menjalankan watch mode dengan default yang longgar, tetapi CI menjalankan konfigurasi yang lebih ketat, Anda akan mengalami kegagalan tak terduga setelah merge. Jaga agar script tetap eksplisit:

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

Walkthrough singkat membantu anggota tim baru mendapatkan baseline yang sama dengan cepat:

Pilihan setup yang paling berdampak adalah disiplin terhadap default. Letakkan alias di config. Letakkan mock environment di satu file setup. Gunakan jsdom untuk UI tests dan environment yang lebih ringan untuk pure utilities jika memungkinkan. Semakin sedikit perilaku kustom yang dibutuhkan oleh setiap test individual, semakin andal sistem Anda.

Menulis component test yang bermakna

Organisasi tidak punya masalah menulis test. Masalahnya adalah menulis test yang masih relevan enam bulan kemudian.

Pola standar untuk unit testing komponen React tetap yang paling tepat: render komponen, query UI dengan selector berpusat pada pengguna, pemicu interaksi, dan assert perubahan DOM yang dihasilkan, yang menjaga test tetap jauh dari detail implementasi seperti state atau props, seperti yang dijelaskan dalam panduan testing React. Kuncinya adalah menerapkan pola tersebut dengan proporsional.

Uji accordion seperti user menggunakannya

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

Perilaku itu sudah cukup untuk beberapa tes yang berguna:

  1. Render awal menampilkan judul, tetapi tidak menampilkan konten.
  2. Mengklik trigger akan menampilkan konten.
  3. Mengklik lagi akan menutupnya.
  4. Atribut aksesibilitas mencerminkan status tampilan.

Poin terakhir ini sering terlewatkan. 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.

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

Pilih query berdasarkan intent

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

Jenis Query Saat Elemen Ditemukan Saat Elemen Tidak Ditemukan Contoh Kasus Penggunaan
getBy Mengembalikan elemen secara langsung Melempar error secara langsung Assert tombol atau heading sudah ada di layar
queryBy Mengembalikan elemen secara langsung Mengembalikan null Assert konten tersembunyi tidak ada sebelum interaksi
findBy Resolves saat elemen muncul Rejects setelah menunggu Assert konten yang dimuat secara async muncul setelah fetch atau update tertunda

Model mental sederhana membantu:

  • Gunakan getBy untuk hal-hal yang harus sudah ada.
  • Gunakan queryBy untuk hal-hal yang belum ada.
  • Gunakan findBy ketika UI berubah di kemudian hari.

Jika sebuah test dimulai dengan findBy untuk semua hal, biasanya berarti penulis tidak yakin kapan komponen diperbarui. Ketidakpastian itu menjadi flakiness di kemudian hari.

Contoh praktis accordion

Berikut adalah komponen representatif:

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>
  );
}

Dan inilah bentuk 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 seperti itu hanya menambah beban maintenance, bukan keyakinan.

Beberapa kebiasaan membuat component test lebih kuat:

  • Utamakan role-based queries: Button, heading, dialog, alert, dan input biasanya harus ditemukan berdasarkan role.
  • Buat setiap test tetap spesifik: Satu perilaku yang terlihat pengguna per pengujian 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 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 Logika Aplikasi

Aplikasi React menyembunyikan banyak perilaku penting di luar komponen. Transisi state ada di hooks. Validasi dan formatting ada di fungsi helper. Pembentukan data sering terjadi sebelum apa pun dirender. Jika Anda hanya menguji komponen yang terlihat, Anda akan melewatkan sebagian besar code yang masih bisa merusak perilaku produksi.

Hooks butuh harness yang React-aware

Custom hook tetap membutuhkan React untuk dieksekusi dengan benar, jadi uji dengan renderHook dan bungkus panggilan yang mengubah state dengan act().

Sebuah useToggle hook 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 kontrak publik:

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 itu berguna karena hook-nya sendiri adalah unitnya. Kamu tidak menguji internal React. Kamu memverifikasi perilaku eksternal hook tersebut.

Bagi tim produk yang membangun UI atau primitif fitur yang dapat digunakan kembali, pola ini sangat penting. Hook sering menjadi antarmuka yang dibagikan di antara aplikasi, design system, atau tooling internal. Jika Anda merancang perilaku yang dapat digunakan kembali dengan niat komersial, sumber daya tentang hook untuk produk pembuat dapat membantu membingkai hook sebagai blok bangunan yang diprodukkan, bukan sekadar detail implementasi.

Logika murni harus tetap murni saat testing

Tidak semua hal membutuhkan jsdom, React, atau Testing Library. Jika sebuah fungsi bersifat murni, uji dengan Jest biasa di lingkungan Node.

Contoh:

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

Tes itu harus sangat sederhana:

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 di sini adalah kecepatan dan kejelasan. Kalau sebuah fungsi tidak butuh tree hasil render, jangan beri. Tooling khusus React menambah overhead. Buat tes logika bisnis tetap kecil, cepat, dan dekat dengan fungsi yang diuji.

Pembagian praktis ini berjalan baik:

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

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 stabil gagal di dua titik: pada boundary dependency, dan pada faktor waktu.

Itulah mengapa async testing dan mocking menjadi pembeda antara suite test mainan dan suite yang bisa Anda percaya sebelum rilis. Sebuah analisis mengaitkan 46,5% flakiness test dengan masalah lingkungan atau resource seperti async timing di analisis unit testing React ini. Dalam 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.

Diagram perbandingan yang menunjukkan Teknik Pengujian React Lanjutan, dengan fokus pada Mocking Dependency vs Async Testing.

Mock boundary, bukan setiap layer

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

Untuk komponen yang mengambil data akun, mock klien jaringan atau modul API. Jangan mock hook, komponen child row, spinner loading, dan tiga fungsi utilitas kecuali tes benar-benar membutuhkan isolasi di titik-titik tersebut.

Gunakan aturan ini:

  • Mock layanan eksternal: Klien HTTP, analitik, API khusus browser, bridge native
  • Mock API platform yang tidak stabil: matchMedia, timer, antarmuka preload Electron, plugin Capacitor jika tidak tersedia di jsdom
  • Hindari mem-mock internal Anda sendiri secara default: hook kustom, children sederhana, utilitas lokal

Jika sebuah test lolos hanya karena bagian yang sulit diganti dengan fakes, itu tidak memberi banyak keyakinan untuk rilis.

Untuk tim yang ingin contoh dan pola seputar API runner, Capgo tutorial pengujian adalah pustaka referensi praktis, terutama saat onboarding developer yang sudah paham React tapi belum memahami mekanika testing.

Test async gagal jika timing tidak jelas

Kegagalan async biasanya berasal dari salah satu dari tiga kesalahan ini:

  1. Test melakukan assert terlalu cepat.
  2. Test menunggu dengan timer yang sembarangan.
  3. Komponen diperbarui lebih dari sekali, tetapi tes hanya memodelkan satu transisi.

Tes async yang stabil biasanya memiliki bentuk 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, ketika Anda perlu menunggu kondisi tertentu:

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

Gunakan findBy ketika kemunculan satu elemen adalah event yang Anda pedulikan. Gunakan waitFor ketika kondisinya lebih luas atau state tidak bisa diekspresikan dengan satu query. Hindari setTimeout dalam tes kecuali Anda secara eksplisit menguji perilaku timer dan menggunakan fake timers.

Ekosistem pengujian React juga mengharapkan Anda mematuhi act() semantik seputar update. Testing Library menangani banyak hal ini untuk Anda, tetapi jika Anda mengatur state secara manual atau memajukan timer, Anda tetap perlu memikirkan kapan update di-flush.

Pahami tool mocking mana yang harus digunakan

Berbagai alat mocking menyelesaikan masalah yang berbeda:

Alat Best use Kesalahan umum
jest.fn() Fake callback standalone atau fungsi yang di-inject Menggunakannya untuk mengganti seluruh modul ketika callback sederhana sudah cukup
jest.spyOn() Mengamati atau meng-override satu method pada objek atau modul asli Lupa mengembalikan implementasi asli
jest.mock() Mengganti dependensi modul di batas import Mock modul besar secara default dan kehilangan perilaku yang bermakna

Contoh membantu:

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

Satu area tingkat lanjut yang jarang dibahas banyak panduan adalah pengujian error-path di React modern. Error boundaries, perubahan state yang tertunda, dan UI fallback async layak diuji dengan serius, bukan sekadar contoh klik “happy path”. Jika child component melempar error, pastikan UI fallback muncul. Jika request gagal, pastikan state pemulihan terlihat. Jika tombol nonaktif saat loading, pastikan itu juga. Itulah bug yang 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 penting. Suite penuh dengan asersi dangkal, snapshot luas, dan internal yang di-mock menciptakan kesan aman sekaligus meningkatkan biaya maintenance.

Infografis yang membandingkan manfaat pengujian kualitas dengan overhead maintenance dari suite test berkuantitas tinggi.

Coverage adalah peta, bukan tujuan akhir

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

Mereka tidak berguna jika mendorong developer untuk menguji wrapper sepele, markup statis, atau file pass-through satu baris hanya demi menaikkan persentase. Perlakukan coverage sebagai alat penemuan. Jika state autentikasi, aksi billing, feature flags, atau prompt update tidak punya test, itu sinyal. Jika komponen ikon presentasional tidak punya test, biasanya itu bukan masalah.

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

  • Ya: Memverifikasi perilaku yang terlihat pengguna di jalur kritis.
  • Mungkin: Melindungi logika bisnis yang mudah rusak saat refactor.
  • Tidak: Mengasumsikan detail implementasi atau menduplikasi nilai test lain.

Apa yang sebaiknya tidak di-unit test

Banyak panduan React masih tidak cukup membahas pengabaian. Kesenjangan ini penting karena over-mocking dan pengujian detail implementasi menciptakan suite rapuh yang lolos sementara pengalaman pengguna tetap rusak, seperti dicatat dalam panduan BrowserStack tentang apa yang tidak perlu di-unit test di React.

Lewati atau batasi ketat pola-pola ini:

  • Asumsi state internal: Jangan uji isOpen secara langsung jika Anda bisa menguji apakah panel terbuka.
  • Perilaku framework: Jangan uji bahwa React memanggil effect. Uji hasil dari perubahan yang dilakukan effect.
  • Internal library pihak ketiga: Uji integrasi Anda dengan date picker atau router, bukan logika rendering library itu sendiri.
  • Unit yang terlalu terpecah: Jika Anda mem-mock setiap child dan helper, Anda mungkin tidak lagi menguji perilaku yang bermakna.

Tes yang buruk lebih buruk daripada tes yang hilang jika mereka menghalangi refactor dan tetap gagal menangkap bug produksi.

Heuristik yang berguna adalah kepemilikan boundary. Uji apa yang dimiliki code Anda. Jangan uji apa yang sudah dimiliki React, browser, atau library yang matang kecuali lapisan integrasi Anda mengubah kontraknya.

Kapan snapshot membantu dan kapan merugikan

Snapshot tidak sia-sia. Mereka hanya mudah disalahgunakan.

Gunakan dengan hemat untuk komponen dengan output stabil dan sederhana di mana diff struktural luas bermakna. Hindari untuk komponen interaktif atau sangat dinamis karena akan menjadi noise. Developer berhenti membacanya dan mulai memperbarui secara refleks.

Alternatif yang lebih baik biasanya ada:

  • Untuk conditional rendering, assert keberadaan atau ketiadaan teks kunci.
  • Untuk perubahan state visual, assert role, label, atau atribut yang penting.
  • Untuk error dan fallback, assert pesan aktual atau area alert.

Jika tim Anda membutuhkan proses kualitas yang lebih luas di luar unit test, pendamping yang solid adalah sebuah workflow quality assurance aplikasi yang memperlakukan tes, pemeriksaan rilis, dan perencanaan rollback sebagai satu sistem. Itulah pergeseran pola pikir yang paling cepat meningkatkan kualitas tes. Berhenti bertanya berapa banyak tes yang Anda miliki. Mulai bertanya kegagalan mana yang masih bisa menjangkau pengguna.

Integrasi Test ke Pipeline CI/CD Cross-Platform

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

Test suite menjadi operasional ketika setiap pull request menjalankan pemeriksaan yang sama di lingkungan bersih dan memblokir merge jika pemeriksaan itu gagal. Kedengarannya jelas, tetapi banyak tim masih meninggalkan celah kritis. Tes dijalankan manual. Laporan coverage opsional. Job packaging dan rilis dimulai sebelum job tes selesai. Begitulah regresi UI kecil berubah menjadi kegagalan rilis yang lebih besar.

Diagram alur lima langkah yang mengilustrasikan proses mengintegrasikan tes otomatis React ke dalam pipeline pengembangan CI/CD.

Pull request harus memicu gate yang sama setiap kali

Agar unit testing React berfungsi seperti jaring pengaman, CI membutuhkan beberapa hal pokok:

  • Jalankan di setiap pull request
  • Instal dependensi dari lockfile
  • Gunakan perintah tes yang sama setiap kali
  • Fail fast saat tes gagal
  • Publikasikan artefak hanya setelah tes lulus

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

Workflow GitHub Actions yang sederhana sudah cukup untuk 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 justru di situlah poinnya. Pipeline terkuat biasanya adalah yang paling tidak mengejutkan.

Mengapa ini lebih penting untuk Capacitor dan Electron

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

Beberapa contoh menunjukkan di mana pipeline membantu:

  • Aplikasi Capacitor: Web code mungkin lolos secara lokal tetapi gagal ketika bridge plugin, status offline, atau edge case siklus hidup aplikasi mengubah perilaku setelah packaging.
  • Aplikasi Electron: Komponen renderer mungkin bergantung pada API preload, pesan window, atau status khusus desktop yang tidak ada dalam pengujian browser biasa kecuali sengaja di-mock.
  • Kereta rilis bersama: Satu bundle yang buruk dapat berdampak pada beberapa target jika proses deployment Anda tidak mengunci publikasi dengan ketat.

Itulah mengapa unit test harus dijalankan sebelum job packaging, dan job packaging harus dijalankan sebelum job distribusi. Setiap tahap menyempitkan risiko. Unit test menangkap regresi lokal dengan cepat. Packaging platform memverifikasi asumsi lingkungan. Persetujuan manual atau staged rollout menangani kepercayaan rilis akhir.

Workflow GitHub Actions yang praktis

Pipeline yang lebih matang biasanya memisahkan tanggung jawab:

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

Untuk tim yang mengirim live update ke Capacitor atau aplikasi Electron, di sinilah release tooling menjadi penting. Salah satu opsi dalam workflow tersebut adalah Capgo, yang mempublikasikan web bundle bertanda tangan untuk aplikasi CapacitorJS dan Electron dengan dukungan rollback dan kontrol rollout berbasis channel. Pada praktiknya, ini berarti job test React Anda dapat berfungsi sebagai gerbang keras pertama sebelum web bundle dipromosikan ke staging atau produksi.

Aturan operasionalnya sederhana. Jangan biarkan infrastruktur rilis mengompensasi tes yang lemah. Gunakan infrastruktur rilis setelah tes yang andal sudah menyaring perubahan buruk.

Sistem pengujian yang andal mengubah perilaku tim. Engineer merge dengan lebih percaya diri. Reviewer fokus pada edge case, bukan menjalankan dasar secara manual. Manajer rilis tidak lagi menganggap setiap deploy sebagai perjudian. Itulah hasil dari melakukan unit testing React dengan baik.


Jika tim Anda merilis React melalui Capacitor atau Electron, keamanan rilis bergantung pada lebih dari sekadar tes lokal yang hijau. Capgo memberikan tim cara terkontrol untuk menerbitkan pembaruan web yang ditandatangani, menargetkan kanal rollout, dan membatalkan bundle yang buruk tanpa menunggu review store, yang cocok di belakang CI pipeline yang sudah mensyaratkan lulus unit test sebelum deployment.

Live updates untuk aplikasi Capacitor

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

Terbaru dari Blog kami

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