Lewati ke konten utama
menjadi.dev
Chapter 19.2 Testing Menengah

Vitest: Unit Testing Super Cepat

Setup dan tulis unit test dengan Vitest — test runner modern untuk Vite

Tujuan Pembelajaran

  • Bisa setup Vitest di proyek
  • Menguasai matcher dan assertion
  • Mengerti mocking dasar

Analogi

Diagram

      Vitest = Test runner untuk Vite ecosystem

Features:
├── Native ESM support
├── TypeScript out of the box
├── Jest-compatible API
├── Watch mode super cepat
└── UI mode untuk debugging
    

Vitest adalah test runner modern yang designed untuk Vite ecosystem — cepat, native ESM, dan TypeScript support tanpa config

Penjelasan Konsep

Jest adalah test runner paling populer di ekosistem JavaScript. Tapi Jest dirilis sebelum ESM menjadi standar dan butuh banyak konfigurasi untuk TypeScript. Vitest datang sebagai alternatif modern yang designed untuk Vite ecosystem.

Kenapa Vitest?

  1. Native ESM: Vitest menggunakan native ES Modules — tidak perlu transformasi
  2. TypeScript built-in: Tulis test dalam.ts tanpa config tambahan
  3. Jest-compatible API: describe, it, expect — semuanya sama
  4. Super cepat: Menggunakan Vite’s transform pipeline — test jalan dalam milidetik
  5. UI Mode: Visual test runner untuk debugging

Setup Vitest

npm install -D vitest @vitest/ui
# atau
bun add -d vitest

Tambahkan ke package.json:

{
  "scripts": {
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:run": "vitest run"
  }
}

Naming Convention

File test bisa dinamai:

  • nama.test.ts — file test terpisah
  • nama.spec.ts — alternatif naming
  • __tests__/nama.ts — di folder tests

Vitest otomatis mencari file yang match pattern: **/*.{test,spec}.{js,ts}

Assertion Matchers

Vitest (dan Jest) punya banyak matcher untuk berbagai kebutuhan:

  • toBe() — strict equality (===)
  • toEqual() — deep equality (untuk object/array)
  • toBeTruthy() / toBeFalsy() — truthy/falsy check
  • toBeNull() / toBeUndefined() — null/undefined
  • toBeGreaterThan() / toBeLessThan() — perbandingan
  • toContain() — array/string contains
  • toThrow() — exception thrown
  • toHaveLength() — panjang array/string
  • toMatch() — regex matching

Grouping dengan describe

Organize test dengan describe yang nested:

describe('UserService',

() => {
  describe('create', () => {
    it('should create user with valid data', () => { ... });
    it('should reject duplicate email', () => { ... });
  });

  describe('findById', () => {
    it('should return user when found', () => { ... });
    it('should return null when not found', () => { ... });
  });
});

Inti yang Perlu Dipahami

Bagian ini berfokus pada bisa setup Vitest di proyek., menguasai matcher dan assertion., dan mengerti mocking dasar. Jangan terburu-buru menghafal istilahnya. Lebih penting untuk memahami peran setiap konsep dan kapan konsep itu muncul dalam pekerjaan web development.

Saat membaca Vitest, gunakan tujuan belajar sebagai penanda arah. Kalau kamu sudah bisa menjelaskan tujuan itu dengan kata-katamu sendiri, berarti fondasinya mulai terbentuk.

Cara Membayangkannya

Vitest adalah test runner modern yang designed untuk Vite ecosystem — cepat, native ESM, dan TypeScript support tanpa config. Analogi ini dipakai supaya konsep teknis tidak terasa melayang. Hubungkan setiap istilah dengan perannya: siapa yang meminta, siapa yang memproses, data apa yang berpindah, dan hasil apa yang diharapkan.

Kalau analoginya sudah terasa masuk akal, barulah lihat istilah teknisnya. Cara ini membuat materi lebih mudah dipahami daripada langsung menghafal definisi.

Saat Melihat Contoh Kode

Contoh kode pada chapter ini memakai bahasa typescript. Bacalah contoh kode sebagai ilustrasi alur, bukan sebagai bagian yang harus langsung dihafal. beforeEach() berjalan sebelum setiap test — setup fresh state. In-memory array sebagai mock database. toEqual() untuk deep comparison object.

Perhatikan nama fungsi, urutan langkah, dan data yang berpindah. Biasanya tiga hal itu sudah cukup untuk memahami hubungan antara teori dan praktik.

Konteks dalam Perjalanan Belajar

Setiap konsep di platform ini dipilih karena dipakai di industri. Fokus pada pemahaman, bukan hafalan.

Kamu sudah di bagian lanjutan. Mulai pikirkan bagaimana konsep ini dipakai di dunia kerja — bukan hanya untuk belajar, tapi untuk membangun produk nyata.

Gunakan pertanyaan reflektif dan prompt AI di akhir chapter sebagai latihan aktif. Membaca saja tidak cukup — kamu perlu menjelaskan ulang dengan kata-katamu sendiri.

Contoh Kode

typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,           // describe, it, expect global (tidak perlu import)
    environment: 'node',     // atau 'jsdom' untuk DOM testing
    include: ['src/**/*.{test,spec}.{js,ts}'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: ['node_modules/', 'src/**/*.d.ts'],
    },
  },
  resolve: {
    alias: {
      '@/': new URL('./src/', import.meta.url).pathname,
    },
  },
});

// UserService test
import { describe, it, expect, beforeEach } from 'vitest';
import { UserService } from './UserService';

// In-memory database untuk test
const users: User[] = [];

describe('UserService', () => {
  let service: UserService;
  
  beforeEach(() => {
    users.length = 0;  // Clear array
    service = new UserService(users);
  });
  
  describe('create', () => {
    it('creates user with valid data', () => {
      const user = service.create({ name: 'Budi', email: 'budi@mail.com' });
      expect(user.id).toBeDefined();
      expect(user.name).toBe('Budi');
      expect(users).toHaveLength(1);
    });
    
    it('rejects duplicate email', () => {
      service.create({ name: 'Budi', email: 'budi@mail.com' });
      expect(() =>
        service.create({ name: 'Ani', email: 'budi@mail.com' })
      ).toThrow('Email already exists');
    });
  });
  
  describe('findById', () => {
    it('returns user when found', () => {
      const created = service.create({ name: 'Budi', email: 'budi@mail.com' });
      const found = service.findById(created.id);
      expect(found).toEqual(created);
    });
    
    it('returns null when not found', () => {
      const found = service.findById(999);
      expect(found).toBeNull();
    });
  });
});

Penjelasan Kode

beforeEach() berjalan sebelum setiap test — setup fresh state. In-memory array sebagai mock database. toEqual() untuk deep comparison object.

Prompt AI

Setup Vitest di proyek Vite + TypeScript-mu. Tulis unit test untuk service layer yang sudah kamu buat (userService, productService, dll).

Pertanyaan Reflektif

Jalankan vitest --ui dan lihat visual test runner. Berapa persen code coverage-mu saat ini?