Pengujian Vue 3 di 2026: Vitest, Vue Test Utils, dan Pertanyaan Wawancara
Panduan praktis pengujian Vue di 2026: konfigurasi Vitest, memasang komponen dengan Vue Test Utils, menguji composable dan store Pinia, memocking API, mengukur coverage, serta pertanyaan wawancara yang diajukan tim perekrut.

Pengujian Vue memisahkan refactor yang percaya diri dari tebakan yang rapuh, dan pada 2026 rantai peralatannya telah menyatu di sekitar dua pustaka: Vitest sebagai runner dan Vue Test Utils untuk memasang komponen. Panduan ini menelusuri cara mengonfigurasi tumpukan tersebut, menguji komponen dan composable, memocking dependensi, serta menjawab pertanyaan wawancara pengujian Vue yang benar-benar diajukan oleh tim perekrut.
Rekomendasi bawaan dari dokumentasi Vue adalah Vitest untuk pengujian unit dan komponen, Vue Test Utils sebagai API pemasangan tingkat rendah, serta Playwright atau Cypress untuk cakupan end-to-end. Vitest menggunakan kembali konfigurasi Vite yang sama dengan aplikasi, sehingga pengujian berjalan melalui pipeline transformasi yang identik dengan hot reload nyaris seketika.
Mengonfigurasi Vitest untuk Proyek Vue 3
Vitest berbagi pipeline Vite, yang berarti satu berkas konfigurasi menggerakkan baik server pengembangan maupun test runner. Opsi environment harus disetel ke jsdom atau happy-dom agar pengujian komponen memiliki DOM tempat merender. Flag globals: true mengekspos describe, it, dan expect tanpa perlu mengimpornya di setiap berkas.
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath } from 'node:url'
export default defineConfig({
plugins: [vue()],
test: {
// jsdom provides document/window so components can mount
environment: 'jsdom',
// expose describe/it/expect globally
globals: true,
// run this before each test file (matchers, cleanup)
setupFiles: ['./tests/setup.ts'],
// only pick up *.spec.ts and *.test.ts files
include: ['**/*.{test,spec}.ts'],
},
resolve: {
alias: {
// mirror the @ alias used across the app
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})Dengan Vitest 3, konfigurasi di atas langsung berfungsi setelah memasang vitest, @vue/test-utils, @vitejs/plugin-vue, dan jsdom. Karena alias mencerminkan konfigurasi aplikasi, impor seperti @/composables/useCart teratasi secara identik di pengujian maupun produksi.
Memasang Komponen dengan Vue Test Utils
Vue Test Utils menyediakan dua titik masuk: mount, yang merender seluruh pohon komponen, dan shallowMount, yang menggantikan komponen anak dengan stub. Untuk sebagian besar pengujian unit, mount lebih dianjurkan karena menguji perilaku render yang sebenarnya. Objek wrapper yang dikembalikan menyediakan pembantu kueri seperti find, get, dan text untuk melakukan asersi terhadap keluaran yang dirender.
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import PriceTag from '@/components/PriceTag.vue'
describe('PriceTag', () => {
it('formats the amount as currency', () => {
const wrapper = mount(PriceTag, {
// props are passed through the mounting options
props: { amount: 4200, currency: 'EUR' },
})
// get() throws if the selector is missing, unlike find()
expect(wrapper.get('[data-test="price"]').text()).toBe('€42.00')
})
it('applies a discount class when on sale', () => {
const wrapper = mount(PriceTag, {
props: { amount: 4200, currency: 'EUR', onSale: true },
})
// classes() returns the array of applied CSS classes
expect(wrapper.get('[data-test="price"]').classes()).toContain('price--sale')
})
})Menargetkan elemen melalui atribut data-test alih-alih kelas CSS menjaga pengujian tetap tangguh: perubahan gaya pada .price--sale tidak akan merusak selektor, hanya perubahan perilaku yang sesungguhnya yang akan berpengaruh. Pemisahan ini merupakan tema yang berulang dalam pengujian Vue yang mudah dipelihara.
Menguji Peristiwa Pengguna dan Peristiwa yang Dipancarkan
Komponen interaktif membutuhkan asersi terhadap apa yang terjadi setelah klik atau input. Vue Test Utils mengembalikan sebuah promise dari trigger, dan menunggunya dengan await akan menuntaskan antrean reaktivitas Vue sehingga DOM mencerminkan pembaruan. Pembantu emitted merekam setiap peristiwa kustom yang dipancarkan komponen, dan inilah cara memverifikasi kontrak antara induk dan anak.
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import SearchBar from '@/components/SearchBar.vue'
describe('SearchBar', () => {
it('emits search with the typed query', async () => {
const wrapper = mount(SearchBar)
// setValue writes to the input and fires an input event
await wrapper.get('input').setValue('vitest')
// awaiting trigger flushes the reactivity queue before assertions
await wrapper.get('[data-test="submit"]').trigger('click')
// emitted() returns a record of every event and its payloads
const events = wrapper.emitted('search')
expect(events).toHaveLength(1)
// events[0] is the first emission, [0] its first argument
expect(events![0][0]).toBe('vitest')
})
})Vue menyatukan pembaruan reaktif secara asinkron. Lupa untuk await pada pemanggilan trigger atau setValue adalah penyebab paling umum pengujian Vue yang tidak stabil: asersi berjalan sebelum DOM dirender ulang, sehingga membaca keluaran yang basi. Ketika perubahan status terjadi di luar pembantu peristiwa, impor nextTick dari vue lalu jalankan await nextTick() sebelum melakukan asersi.
Menguji Composable Vue Secara Terisolasi
Composable yang hanya menggunakan API reaktivitas (ref, computed, watch) sama sekali tidak memerlukan komponen: ia hanyalah fungsi biasa yang mengembalikan status reaktif, sehingga memanggilnya secara langsung dan melakukan asersi pada .value sudah memadai. Ini adalah cara tercepat dan paling terfokus untuk mencakup logika bisnis, dan cocok dipadukan dengan pola-pola dalam composable Vue 3 tingkat lanjut.
import { describe, it, expect } from 'vitest'
import { useCart } from '@/composables/useCart'
describe('useCart', () => {
it('tracks total price as items change', () => {
// composables using only ref/computed run without mounting
const { items, total, addItem } = useCart()
expect(total.value).toBe(0)
addItem({ id: 1, price: 1500, qty: 2 })
// computed total recalculates synchronously on state change
expect(total.value).toBe(3000)
addItem({ id: 2, price: 500, qty: 1 })
expect(items.value).toHaveLength(2)
expect(total.value).toBe(3500)
})
})Composable yang bergantung pada hook siklus hidup seperti onMounted merupakan pengecualian: ia harus berjalan di dalam instansi komponen. Solusi umum adalah komponen pembantu kecil yang memanggil composable di dalam setup dan mengekspos hasilnya, lalu memasang pembantu tersebut dengan Vue Test Utils.
Siap menguasai wawancara Vue.js / Nuxt.js Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Memocking Panggilan API dengan vi.mock
Permintaan jaringan yang sungguhan membuat pengujian menjadi lambat dan tidak deterministik. Vitest mengganti modul dengan vi.mock serta membuat fungsi mata-mata dengan vi.fn. Pola di bawah ini memocking modul yang membungkus fetch, sehingga composable yang diuji menerima data terkendali tanpa pernah menghubungi server.
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useProducts } from '@/composables/useProducts'
import { getProducts } from '@/api/products'
// replace the whole module with mocked exports
vi.mock('@/api/products', () => ({
getProducts: vi.fn(),
}))
describe('useProducts', () => {
beforeEach(() => {
// reset call history between tests to avoid leakage
vi.mocked(getProducts).mockReset()
})
it('exposes fetched products and clears loading', async () => {
// define what the mocked API returns for this test
vi.mocked(getProducts).mockResolvedValue([
{ id: 1, name: 'Keyboard' },
])
const { products, loading, load } = useProducts()
await load()
expect(getProducts).toHaveBeenCalledOnce()
expect(loading.value).toBe(false)
expect(products.value[0].name).toBe('Keyboard')
})
})Memocking pada batas modul menjaga logika internal composable tetap utuh sekaligus memberi setiap pengujian kendali penuh atas respons API, termasuk jalur galat. Memanggil mockRejectedValue sebagai gantinya memungkinkan satu pengujian memverifikasi bahwa kegagalan menyetel status galat dan menghentikan indikator pemuatan.
Menguji Store Pinia di 2026
Store menyimpan status yang dibagikan antarkomponen, dan paket resmi @pinia/testing membuatnya mudah untuk diuji. createTestingPinia secara bawaan mengganti setiap action dengan stub, sehingga pengujian komponen dapat memastikan sebuah action telah dikirim tanpa menjalankan efek sampingnya. Panduan pengujian Pinia merekomendasikan pendekatan ini untuk pengujian tingkat komponen.
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'
import CheckoutButton from '@/components/CheckoutButton.vue'
import { useCartStore } from '@/stores/cart'
describe('CheckoutButton', () => {
it('dispatches checkout on click', async () => {
const wrapper = mount(CheckoutButton, {
global: {
plugins: [
// vi.fn spies on actions instead of executing them
createTestingPinia({ createSpy: vi.fn }),
],
},
})
const store = useCartStore()
await wrapper.get('[data-test="checkout"]').trigger('click')
// the action is stubbed, so only the dispatch is asserted
expect(store.checkout).toHaveBeenCalledOnce()
})
})Untuk menguji logika store itu sendiri alih-alih integrasi komponen, teruskan stubActions: false agar action berjalan normal, lalu lakukan asersi terhadap status yang dihasilkan. Pemisahan antara pengujian komponen dengan action yang di-stub dan pengujian store dengan action nyata ini menjaga setiap suite tetap terfokus pada satu tanggung jawab.
Menjalankan Pengujian dalam Mode Watch dan Mengukur Coverage
Vitest menyertakan mode watch yang menjalankan ulang hanya pengujian yang terpengaruh oleh berkas yang disimpan, sehingga memperpendek putaran umpan balik selama pengembangan hingga sepersekian detik. Pelaporan coverage terpasang secara bawaan melalui provider @vitest/coverage-v8 dan terintegrasi mulus ke dalam pipeline integrasi berkelanjutan, di mana pengujian yang gagal atau regresi coverage dapat memblokir penggabungan.
export default defineConfig({
test: {
coverage: {
// v8 is the fastest provider and needs no instrumentation step
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
// fail CI when a threshold drops below the target
thresholds: {
statements: 80,
branches: 75,
functions: 80,
},
// exclude generated and config files from the report
exclude: ['**/*.config.ts', '**/types/**'],
},
},
})Menjalankan vitest saja memulai mode watch secara lokal, sedangkan vitest run --coverage mengeksekusi satu kali jalan yang cocok untuk CI. Menetapkan ambang batas eksplisit mengubah coverage dari metrik pemanis menjadi gerbang mutu yang ditegakkan: sebuah pull request yang menurunkan coverage cabang di bawah 75 persen akan gagal secara otomatis, yang mendorong kontributor untuk menguji jalur yang mereka tambahkan, bukan hanya jalur bahagia.
Pertanyaan Wawancara Pengujian Vue yang Umum Beserta Jawabannya
Pertanyaan seputar pengujian muncul di hampir semua wawancara Vue tingkat senior karena mengungkap cara kandidat berpikir tentang kemudahan pemeliharaan. Jawaban di bawah ini mencakup konsep yang paling sering muncul, dan set lengkapnya dilatih dalam modul wawancara pengujian Vue.
Kapan shallowMount sebaiknya digunakan alih-alih mount? Gunakan shallowMount ketika sebuah komponen merender anak yang berat atau yang sudah teruji dan pengujian hanya peduli pada logika induknya sendiri. Menggantikan anak dengan stub mengisolasi unit dan mempercepat render, dengan konsekuensi tidak memverifikasi integrasi nyata antarkomponen.
Bagaimana pembaruan asinkron ditangani di Vue Test Utils? Vue menerapkan perubahan reaktif pada tick berikutnya. Pembantu peristiwa seperti trigger dan setValue mengembalikan promise yang selesai setelah DOM diperbarui, sehingga menunggunya dengan await sudah cukup. Untuk status yang diubah di luar pembantu tersebut, await nextTick() memaksa antrean tuntas sebelum asersi berjalan.
Apa perbedaan antara find dan get? find mengembalikan wrapper kosong ketika tidak ada elemen yang cocok dan tidak pernah melempar galat, yang cocok untuk asersi seperti expect(wrapper.find('.error').exists()).toBe(false). get langsung melempar galat pada elemen yang tidak ada, menjadikannya pilihan yang lebih baik ketika elemen tersebut memang diharapkan ada dan ketiadaannya merupakan kegagalan yang sesungguhnya.
Mengapa lebih memilih atribut data-test daripada selektor kelas? Nama kelas berubah seiring gaya dan struktur, sehingga pengujian yang mengueri mereka akan rusak pada penyuntingan yang bersifat kosmetik. Kait data-test khusus mengungkapkan niat pengujian secara eksplisit dan bertahan melewati refactor, sehingga mengurangi hasil negatif palsu.
Bagaimana cara menguji composable yang bergantung pada onMounted? Hook siklus hidup hanya menyala di dalam instansi komponen, sehingga composable tidak dapat dipanggil langsung. Teknik standarnya adalah komponen harness sekali pakai yang memanggil composable di dalam setup dan mengekspos nilai kembaliannya, lalu memasang harness tersebut dengan Vue Test Utils dan melakukan asersi melalui wrapper. Cara ini menjaga tautan reaktivitas dan siklus hidup tetap utuh sekaligus tetap mengisolasi logika yang diuji. Lebih banyak pertanyaan konseptual dan koding dikumpulkan dalam panduan pertanyaan wawancara Vue.js esensial.
Kesimpulan
Penyiapan pengujian Vue yang andal di 2026 bermuara pada beberapa pilihan yang disengaja:
- Jalankan Vitest dengan
environment: 'jsdom'dan plugin Vue agar pengujian berbagi pipeline transformasi Vite yang persis sama dengan aplikasi. - Lebih memilih
mountdaripadashallowMountkecuali sebuah komponen anak berat atau sudah tercakup di tempat lain. - Selalu
awaitpadatrigger,setValue, dannextTicksebelum melakukan asersi untuk menghilangkan pengujian yang tidak stabil dan bergantung pada waktu. - Uji composable yang hanya menggunakan API reaktivitas dengan memanggilnya langsung dan melakukan asersi pada
.value. - Mocking pada batas modul dengan
vi.mockagar perilaku jaringan deterministik dan jalur galat dapat diuji. - Gunakan
createTestingPinia({ createSpy: vi.fn })untuk pengujian komponen danstubActions: falseketika menguji logika store. - Ueri elemen melalui atribut
data-testagar pengujian hanya rusak pada perubahan perilaku yang nyata, tidak pernah pada penataan gaya.
Kuasai pola-pola ini dan pengujian akan menjadi alat perancangan alih-alih beban, menangkap regresi sebelum mencapai produksi. Jelajahi jalur pembelajaran Vue dan Nuxt selengkapnya untuk terus membangun keterampilan yang siap wawancara.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

Vue 3 Composables Tingkat Lanjut: Pola yang Dapat Digunakan Ulang dan Pertanyaan Interview 2026
Panduan lengkap Vue 3 composables tingkat lanjut: pola yang dapat digunakan ulang, penanganan asinkron, dependency injection, validasi form, pengujian terisolasi, dan pertanyaan interview teknis 2026.

Vue 3 Pinia vs Vuex: Panduan Lengkap State Management dan Pertanyaan Interview 2026
Perbandingan mendalam Pinia vs Vuex: arsitektur, TypeScript support, Composition API, performa, strategi migrasi, dan pertanyaan interview Vue state management 2026.

Nuxt 4 di Tahun 2026: Struktur Direktori Baru dan Panduan Migrasi dari Nuxt 3
Panduan lengkap Nuxt 4 mencakup struktur direktori app/, singleton data fetching, shallow reactivity, serta langkah migrasi bertahap dari Nuxt 3 untuk developer Vue.js.