Saya rasa Anda mungkin berada di salah satu situasi berikut. Entah proyek JavaScript Anda hampir tidak memiliki tes dan setiap refactor terasa berisiko, atau Anda sudah memiliki tes dan setengah dari mereka lambat, rapuh, dan sulit dipercaya.
Hal itu semakin buruk di Capacitor and Electron apps. A simple feature can touch shared business logic, browser APIs, native plugins, local files, IPC, and remote services in the same flow. If you test those pieces the wrong way, your suite becomes a maze of fake dependencies. If you test them the right way, you get fast feedback on the logic that breaks.
Good unit tests JavaScript work doesn’t start with clever matcher syntax. It starts with a disciplined boundary: test pure logic directly, isolate side effects, and avoid writing tests that collapse the moment you rename an internal function.
Mengapa framework bukanlah optional
- Memilih Framework Pengujian JavaScript
- Pengaturan Proyek dan Ujian Pertama Anda
- Menguasai Mock dan Asynchronous Code
- Strategi Lanjutan untuk Ujian yang Kuat
- Tes untuk CI, Capacitor, dan Aplikasi Electron
- Pertanyaan yang Sering Diajukan Tentang Pengujian Satuan JavaScript
Pemilihan Framework Pengujian JavaScript
Proyek JavaScript profesional membutuhkan pengujian yang nyata. Skrip ad hoc dan pengecekan manual di konsol tidak dapat berkembang ketika banyak insinyur menyentuh kode yang sama. Anda membutuhkan penemuan tes, asertasi, penanganan async, mock, dan cara menjalankan semuanya secara konsisten dalam pengembangan lokal dan CI.
Guidance saat ini semakin berkonvergensi pada beberapa pilihan mainstream. Jest, Mocha, dan Jasmine seringkali dianggap sebagai kerangka utama, dengan Jest Dapat seringkali disebut sebagai struktur tes bawaan, asertasi, pemalsuan, dan dukungan async dalam satu paket, seperti yang ditunjukkan di sini Lab Tes JavaScript Pluralsight.

Mengapa kerangka bukanlah hal yang opsional
Kesalahan pertama tim membuat adalah menganggap unit tes sebagai kegiatan sampingan. Biasanya hal ini menyebabkan penamaan file tidak konsisten, asertasi kustom yang tidak diingat, dan bantuan yang hanya dipahami oleh satu orang.
Kerangka memberikan Anda bahasa yang sama:
- Struktur tes dengan
describedantestatauit - klaim dengan matcher yang dapat dibaca
- Hook untuk pengaturan dan penghapusan
- dukungan asinkron untuk janji dan timer
- alat bantu mock untuk dependensi eksternal
Jika tim Anda juga membutuhkan pandangan yang lebih luas tentang otomatisasi tes di luar tingkat unit, Capgo memiliki ringkasan yang berguna tentang tes otomatis dalam alur kerja pengiriman aplikasi.
Mocha vs Jest secara singkat
Jest dan Mocha mewakili dua filosofi yang berbeda.
Mocha adalah pilihan semua dalam satu. Ini membawa kebanyakan dari apa yang dibutuhkan tim pada hari pertama.
Jest Lebih modular. Ini memberikan Anda runner dan mengharapkan Anda untuk menyusun sisa stack.
| Mocha | Mocha | Jest |
|---|---|---|
| Kompleksitas Konfigurasi | Lebih rendah untuk sebagian besar tim | Lebih tinggi karena biasanya Anda menambahkan library asertasi dan mocking |
| Assertions | Dibangun dalam | Sering digunakan bersamaan dengan library lain |
| Mocking | Dibangun dalam | Sering digunakan bersamaan dengan library lain |
| Pengujian Asinkron | Terintegrasi dan sederhana | Dapat didukung, tetapi tergantung lebih pada pengaturan sekitar |
| Alur Kerja Coverage | Sering diintegrasikan ke dalam toolchain yang sama | Sering lebih terpisah-pisah |
| Pilihan Terbaik | Proyek baru, tim yang ingin konsistensi | Stack lama, tim yang ingin kontrol modular |
Aturan praktis: Jika tim Anda harus bertanya-tanya perpustakaan asertasi dan perpustakaan mocking mana yang harus dipasangkan dengan runner, Anda mungkin ingin menggunakan Jest.
Rekomendasi saya untuk tim kebanyakan
Untuk proyek modern kebanyakan, saya akan memilih Jest kecuali kodebase sudah memiliki alasan kuat untuk tetap menggunakan Mocha. Rekomendasi ini semakin kuat ketika aplikasi mencakup Capacitor or Jestkecuali kodebase sudah memiliki alasan kuat untuk tetap menggunakan Mocha. Rekomendasi ini semakin kuat ketika aplikasi mencakup
Mocha still makes sense in older Node.js services or long-lived codebases where the ecosystem around it is already settled. But for a mid-level engineer setting up a robust suite from scratch, Jest usually removes more friction than it creates.
Catatan penting tentang ruang lingkup. Cypress dan Playwright adalah alat yang sangat baik, tetapi mereka menyelesaikan masalah yang berbeda. Mereka lebih baik untuk pengujian browser-level dan end-to-end, bukan loop dalam yang cepat di mana pengujian unit JavaScript seharusnya berada.
Penyiapan Proyek dan Pengujian Pertama
Pengaturan pengujian yang bersih haruslah membosankan. Jika menambahkan pengujian pertama terasa rumit, maka suite pengujian mungkin tidak akan tetap sehat.

Pengaturan sederhana untuk Jest
Mulai dengan proyek JavaScript yang sudah memiliki package.json Kemudian tambahkan Jest sebagai dependensi pengembangan dan hubungkan skrip pengujian.
{
"scripts": {
"test": "jest"
}
}
Itu sudah cukup untuk banyak proyek. Anda dapat menambahkan konfigurasi lebih lanjut jika sistem modul, transpilasi, atau struktur monorepo memerlukan.
Jika Anda sedang membangun aplikasi Capacitor secara lokal dan ingin lingkungan pengembangan Anda dalam kondisi sebelum menambahkan pengujian di sekitar logika yang dibagikan, Capgo's guide untuk menyiapkan lingkungan lokal Capacitor adalah teman yang sangat berguna.
Menulis tes sebelum code
Polanya tes pertama bukan hanya preferensi pribadi. Biro Perlindungan Konsumen Keuangan Amerika Serikat secara eksplisit merekomendasikan menulis tes pertama, mengorganisir tes dengan describe , dan it,dan pengecekan kerangka sekitar expect(...) , dan mengelilingi periksa sekitar klaim dalam panduan tes unit JavaScriptnya.
itu penting karena perubahan tes pertama mengubah cara Anda merancang code. Fungsi cenderung menjadi lebih kecil, dependensi menjadi lebih terlihat, dan efek sampingan berhenti mengalir ke logika yang seharusnya tetap murni.
Contoh minimal berikut:
// math.js
function addTax(amount, rate) {
return amount + amount * rate;
}
module.exports = { addTax };
// math.test.js
const { addTax } = require('./math');
describe('addTax', () => {
it('returns the amount with the tax applied', () => {
expect(addTax(100, 0.2)).toBe(120);
});
});
Pakai Arrange Act Assert setiap kali
Contoh Urutan, Aksi, Konfirmasi Polanya menjaga tes tetap mudah dibaca, bahkan ketika mereka semakin kompleks.
- Urutan masukan dan setiap setup yang dibutuhkan.
- Aksi dengan memanggil fungsi.
- Konfirmasi pada hasilnya.
Aplikasi pada helper validasi:
function isSupportedPlatform(platform) {
return ['ios', 'android', 'web', 'desktop'].includes(platform);
}
describe('isSupportedPlatform', () => {
it('returns true for ios', () => {
// Arrange
const platform = 'ios';
// Act
const result = isSupportedPlatform(platform);
// Assert
expect(result).toBe(true);
});
});
Tes kecil bertahan lama. Tes biasanya harus menjawab satu pertanyaan, bukan menceritakan seluruh alur kerja.
Untuk Capacitor dan proyek Electron, disiplin itu lebih penting karena logika murni Anda sering kali berada di samping integrasi native atau desktop code. Jaga aturan bisnis tetap dapat diuji tanpa runtime platform, dan tes pertama Anda tidak akan menjadi tes terakhir yang berguna.
Penguasaan Mocks dan Asinkron Code
Masalah besar dalam aplikasi code tidak berasal dari penjumlahan dua angka. Mereka berasal dari code yang mencapai luar dirinya: permintaan jaringan, file, API plugin, timer, saluran IPC, lapisan penyimpanan.
Di mana mocking membantu. Ini memberi Anda kontrol atas batasan sehingga tes dapat fokus pada keputusan code.

Mock batasan, bukan semuanya
Panduan tes yang dapat dipertahankan menekankan penutupan perilaku tunggal dan Satu asseri kuat per tesdan juga mengingatkan bahwa menggunakan mock yang berlebihan membuat tes menjadi rapuh dan terikat pada detail implementasi, seperti yang disingkatkan dalam ini Artikel TestRail tentang unit test yang dapat dipelihara.
That warning matters a lot in JavaScript. Teams often start by mocking every imported module and end up testing whether functions call other functions in the “correct” order, instead of testing real behavior.
Target yang salah untuk tes yang berat mock:
- apakah helper A memanggil helper B
- apakah layanan C memanggil serializer D
- apakah fungsi internal privat berjalan dua kali
Target yang lebih baik:
- apa yang dikembalikan oleh fungsi
- apakah itu menangani ketergantungan yang gagal dengan benar
- apakah itu mengubah data menjadi bentuk yang diharapkan
Polanya yang lebih baik untuk Capacitor dan Electron code
Pada aplikasi mobile dan desktop, saya lebih suka layer penutup di sekitar API native atau platform. Kemudian unit tests memalsukan layer penutup, bukan platform itu sendiri.
Struktur contoh:
// cameraGateway.js
async function getPhoto(cameraPlugin) {
return cameraPlugin.getPhoto();
}
module.exports = { getPhoto };
// profilePhotoService.js
async function loadProfilePhoto(cameraGateway) {
const photo = await cameraGateway.getPhoto();
return { path: photo.path, ready: true };
}
module.exports = { loadProfilePhoto };
// profilePhotoService.test.js
const { loadProfilePhoto } = require('./profilePhotoService');
test('returns mapped photo data', async () => {
const fakeCameraGateway = {
getPhoto: jest.fn().mockResolvedValue({ path: '/tmp/pic.jpg' })
};
const result = await loadProfilePhoto(fakeCameraGateway);
expect(result).toEqual({ path: '/tmp/pic.jpg', ready: true });
});
Polanya itu juga berlaku untuk Electron. Tutup ipcRenderer, akses file, atau integrasi shell di balik adapter tipis. Unit tests menghantam layer layanan, bukan runtime secara langsung.
For tim tim yang menguji logika rilis dan jalur pembaruan di aplikasi Capacitor, Capgo memiliki panduan relevan tentang menguji pembaruan OTA Capacitor dengan skenario palsu.
Langkah-langkah singkat membantu jika tim Anda masih memperbaiki gaya tes async:
Menguji aliran async tanpa fluktuasi
Pakai async/await dalam tes ketika code yang diuji kembali janji. Lebih jelas daripada pola callback berat dan lebih mudah untuk di-debug.
async function fetchProfile(api) {
const response = await api.getUser();
return response.name;
}
test('returns the user name from the API response', async () => {
const api = {
getUser: jest.fn().mockResolvedValue({ name: 'Ava' })
};
const result = await fetchProfile(api);
expect(result).toBe('Ava');
});
Juga uji jalur gagal:
test('throws when the API request fails', async () => {
const api = {
getUser: jest.fn().mockRejectedValue(new Error('network failed'))
};
await expect(fetchProfile(api)).rejects.toThrow('network failed');
});
Uji coba baik jalur yang sukses maupun jalur yang tidak. Pada produksi, jalur yang tidak biasanya yang diingat oleh pengguna.
Strategi Lanjutan untuk Tes yang Kuat
Sebuah suite tes menjadi berguna ketika tetap berguna setelah code berubah. Itu lebih sulit daripada menulis tumpukan tes yang berhasil.

Pakai pembagian tes sebagai anggaran
Petunjuk panduan nyata merekomendasikan sebuah 70/20/10 terbagi di unit, integrasi, dan uji akhirDengan unit test yang memberikan feedback paling cepat dan gagal yang paling stabil. Panduan yang sama menyatakan bahwa suatu suite unit yang lengkap seharusnya selesai dalam di bawah 10 detik, dan cek pre-commit harus tetap di bawah 5 detik, menurut petunjuk uji ini Petunjuk Pengujian OpenReplay.
Saya menganggap itu sebagai alat pengaturan anggaran, bukan agama. Jika sebagian besar upaya Anda berada di uji akhir, tim Anda akan menunggu terlalu lama untuk mendapatkan feedback. Jika segalanya adalah unit-only, Anda akan melewatkan batasan sistem nyata.
Pada sebuah aplikasi Capacitor atau Electron, keseimbangan yang sehat biasanya terlihat seperti ini:
- Uji Unit untuk logika harga, aturan akses, serialisasi, kelayakan pembaruan, flag fitur, dan transformasi keadaan
- Uji Integrasi untuk adapter penyimpanan, pembungkus plugin, dan kontrak IPC
- E2E tests untuk beberapa perjalanan kritikal seperti login, alur pembelian, sinkronisasi, atau permintaan pembaruan
Koverasi bukanlah target, melainkan lampu sorot
Rapor koverasi berguna ketika membantu Anda menemukan cabang yang belum diuji dalam logika penting. Mereka menjadi berbahaya ketika tim mengejar persentase koverasi untuk kepentingan mereka sendiri.
Validator login dengan uji kasus tepat memberikan nilai lebih daripada file yang tercover penuh dengan asertasi trivial. Hal itu terutama benar untuk input-heavy code seperti formulir, parser, logika tanggal, dan periksa akses. validasi formulir frontend adalah sebuah komplement yang baik untuk strategi pengujian unit.
Tetes perilaku bertahan melalui refactor
A reliable suite should let you refactor internals without rewriting half the tests. The easiest way to get there is to assert perilaku yang dapat diamati bukan detail implementasi.
Kasus penggunaan yang masih relevan:
- kondisi batas masukan kosong, nilai kosong, jenis data tidak valid, dan string terlalu panjang
- hasil domain seperti “ditolak kembali karena izin tidak ada”
- transisi keadaan seperti “mengubah status sebagai menunggu setelah metadata diunduh diverifikasi”
Kasus penggunaan yang sering rusak:
- memeriksa panggilan helper internal
- menegaskan urutan metode privat
- mengganggu setiap lapisan dalam rantai panggilan
Untuk tim aplikasi yang membangun proses rilis yang disiplin, artikel Capgo tentang jaminan kualitas aplikasi bermanfaat karena menghubungkan pekerjaan tes dengan pipa rilis yang lebih luas.
Tes untuk CI, Capacitor, dan Aplikasi Electron
Tes yang hanya berjalan di satu mesin pengembang bukanlah jaring pengaman. Itu adalah kebiasaan lokal.
CI mengubah pekerjaan tes JavaScript menjadi infrastruktur tim. Setiap push, pull request, atau cabang rilis dapat menguji perintah yang sama dengan harapan yang sama. Konsistensi ini lebih penting lagi untuk Capacitor dan proyek Electron, di mana pergeseran lingkungan menyebabkan gagal yang halus.
Jadikan CI sebagai jalur eksekusi default
Paling tidak, CI Anda harus menginstal dependensi dan menjalankan suite unit pada setiap set perubahan. Simpan perintah identik dengan pengembangan lokal saat mungkin.
Sebuah alur kerja dasar GitHub dapat sekecil ini:
name: test
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
Cukup untuk menangkap import yang rusak, asseri yang gagal, dan asumsi platform yang tidak sengaja sebelum mereka mendarat di utama.
Untuk tim mobile yang mengirimkan melalui pipa otomatis, Capgo memiliki panduan yang praktis untuk mengatur CI/CD untuk aplikasi Capacitor.
Menguji interaksi plugin Capacitor
Jalan yang salah untuk menguji unit Capacitor code adalah dengan menarik plugin native secara langsung ke dalam setiap layanan. Hal ini membuat suite uji Anda terikat pada jembatan platform.
Polanya yang lebih baik adalah abstraksi tipis:
// deviceStorage.js
async function saveFile(filesystem, path, data) {
return filesystem.writeFile({ path, data });
}
module.exports = { saveFile };
// draftService.js
async function persistDraft(storage, draft) {
await storage.save('draft.json', JSON.stringify(draft));
return { saved: true };
}
module.exports = { persistDraft };
// draftService.test.js
const { persistDraft } = require('./draftService');
test('persists a serialized draft', async () => {
const storage = {
save: jest.fn().mockResolvedValue(undefined)
};
const result = await persistDraft(storage, { title: 'Hello' });
expect(result).toEqual({ saved: true });
});
Pemikiran yang sama berlaku untuk akses kamera, prompt biometrik, registrasi token push, dan status jaringan. Tahan panggilan plugin di adapter. Uji logika aplikasi terhadap interface yang Anda kendalikan.
Menguji code Electron main renderer dan IPC
Aplikasi Electron memiliki dua sambungan penting: proses utama code dan proses renderer codeproses renderer __CAPGO_KEEP_0__
. Jangan memadukannya dalam uji coba.
- Unit Test Renderer untuk model tampilan, keadaan, pengaturan format, dan logika bisnis sisi UI
- Unit Test Proses Utama untuk menu, operasi file, dan keputusan siklus aplikasi
- Tes Kontrak IPC untuk bentuk pesan dan respons yang diharapkan
Contoh Wrapper IPC:
// ipcGateway.js
function sendSettings(ipcRenderer, payload) {
ipcRenderer.send('settings:update', payload);
}
module.exports = { sendSettings };
// ipcGateway.test.js
const { sendSettings } = require('./ipcGateway');
test('sends settings update over ipc', () => {
const ipcRenderer = { send: jest.fn() };
sendSettings(ipcRenderer, { theme: 'dark' });
expect(ipcRenderer.send).toHaveBeenCalledWith('settings:update', { theme: 'dark' });
});
Jika Anda kemudian mengubah implementasi internal dari satu bantuan ke bantuan lain, tes ini masih berlaku karena memverifikasi perilaku yang penting. Itulah standar yang Anda inginkan di desktop dan mobile code.
Pertanyaan Tidak Sering Ditanyakan Tentang Unit Testing JavaScript
Apa perbedaan antara unit integration dan E2E tests
A pengujian unit menguji satu bagian logika kecil secara isolasi. Sebuah menguji integrasi menguji apakah beberapa komponen atau layanan bekerja bersama-sama dengan benar. Sebuah menguji akhir ke akhir menguji perjalanan pengguna melalui aplikasi yang berjalan.
Gunakan unit test untuk kepercayaan cepat dalam aturan bisnis. Gunakan test integrasi untuk celah seperti penyimpanan, wrapper plugin, dan IPC. Gunakan E2E test dengan sedikit untuk alur kerja yang akan sangat terganggu jika mereka rusak.
Apakah kita harus berusaha mencapai coverase penuh
Tidak. Coverase penuh dapat mendorong tim ke arah test yang tidak berharga.
Coverase berguna ketika menunjukkan risiko code yang belum pernah diuji. Tidak berguna ketika insinyur menambahkan asertasi dangkal hanya untuk memuaskan dashboard. Jika suite Anda sangat rapuh, coverase yang lebih banyak tidak akan menyelamatkannya.
Bagaimana kita menambahkan test ke basis kode yang sudah ada
Mulai dari tempat perubahan sudah terjadi. Jangan membuat tim beku dan mengumumkan rencana besar untuk merewrite strategi test.
Sebuah urutan praktis seperti ini:
- Lindungi code aktif terlebih dahulu dengan menambahkan tes ke modul yang Anda sentuh selama pekerjaan fitur atau perbaikan bug
- Ekstrak logika murni dari file yang sulit diuji sehingga aturan bisnis dapat diuji tanpa kebisingan framework atau runtime
- Tambahkan wrapper jahit sekitar plugin native, klien jaringan, panggilan filesystem, dan IPC Electron
- Tolak pola yang rapuh ketika memperkenalkan mock. Pedoman dari praktik terbaik pengujian JavaScript terutama berguna di sini karena menyoroti masalah yang sering terlewatkan dari over-mocking dan tes yang rapuh yang mengikuti
Tujuan bukanlah keterpaduan segera. Itu adalah perbaikan yang stabil di tempat-tempat di mana regresi menghabiskan tim paling banyak
Jika tim Anda mengirimkan Capacitor atau Electron Aplikasi-apliksi dan memerlukan proses rilis yang lebih bersih seputar perubahan JavaScript, Capgo adalah salah satu pilihan untuk dilihat. Ini menyediakan pembaruan live untuk aplikasi CapacitorJS dan Electron, dengan kontrol rollout dan observabilitas, sehingga tim dapat memadukan unit testing yang kuat dengan jalur yang lebih aman untuk mengirimkan perubahan bundle web tanpa harus menunggu tinjauan toko untuk setiap perbaikan.