Development

Kapan Unit Testing Jadi Penting Banget: Perjalanan Seorang Developer

Asep Alazhari

Temukan kenapa unit testing itu penting lewat pengalaman production failure yang nyata dan pelajari kapan harus implementasi tests di project Next.js lo.

Kapan Unit Testing Jadi Penting Banget: Perjalanan Seorang Developer

Bangun dari Mimpi: Ketika Abaikan Tests Itu Merugikan Lo

Jujur aja nih, dulu gue mikir unit testing itu buang-buang waktu doang. Ngapain bikin tests kalau gue bisa langsung ngirim fitur aja? Rasanya kayak ngerjain kerjaan dobel gitu: pertama nulis logic-nya, terus nulis tests buat logic yang sama. Otak gue udah capek banget dari nyelesain masalah kompleks, eh masih harus nulis tests lagi? Rasanya… mubazir banget.

Mindset itu jalan bagus kok, sampai nggak jalan lagi.

Suatu hari, klien gue minta peningkatan buat Fitur A, sebuah fungsi yang kompleks banget dengan banyak percabangan kondisi. Kebutuhannya keliatan jelas sih, jadi gue langsung tambahin kondisi baru, tes manual (atau gitu deh gue pikir), terus push ke production. Dalam beberapa jam, tiket support langsung banjir masuk. Kondisi lama yang udah jalan sempurna selama berbulan-bulan? Tiba-tiba rusak. Pengguna nggak bisa nyelesain alur kerja mereka. Fitur baru gue malah tanpa sengaja ngancurin fungsi yang udah ada.

Yang bikin kaget? Gue nggak bisa test semuanya manual. Fiturnya udah tumbuh terlalu kompleks dengan kasus tepi yang buanyak banget, verifikasi manual setiap skenario bisa makan waktu berjam-jam. Pas itu gue nyadar: Gue butuh unit tests, dan gue butuhnya kemarin.

Memahami Unit Testing: Apa Sih Sebenarnya

Unit testing bukan soal nulis tests buat setiap baris kode. Ini tentang mendokumentasikan perilaku dan bikin jaring pengaman buat codebase lo. Bayangin unit tests itu kayak kontrak kode lo, mereka nentuin apa yang fungsi, komponen, sama modul lo harus lakuin di berbagai kondisi.

Unit test fokus test satu “unit” kode secara terpisah, biasanya sebuah fungsi, metode, atau komponen. Tujuannya memverifikasi kalau unit ini berperilaku dengan benar ketika dikasih input atau kondisi tertentu.

// Example: A simple utility function
export function calculateDiscount(price, discountPercent) {
    if (price < 0 || discountPercent < 0) {
        throw new Error("Values must be positive");
    }
    if (discountPercent > 100) {
        throw new Error("Discount cannot exceed 100%");
    }
    return price - (price * discountPercent) / 100;
}

// Unit test for this function
describe("calculateDiscount", () => {
    it("should calculate discount correctly", () => {
        expect(calculateDiscount(100, 20)).toBe(80);
    });

    it("should throw error for negative price", () => {
        expect(() => calculateDiscount(-100, 20)).toThrow("Values must be positive");
    });

    it("should throw error for discount over 100%", () => {
        expect(() => calculateDiscount(100, 150)).toThrow("Discount cannot exceed 100%");
    });
});

Baca Juga: Server Actions vs Client Rendering di Next.js: Panduan Developer 2025

Kapan Unit Testing Jadi Wajib Banget

Dari pengalaman yang menyakitkan banget, gue udah identifikasi skenario spesifik dimana unit testing bukan cuma membantu, tapi wajib banget:

1. Logika Bisnis yang Kompleks

Kalau kode lo punya banyak pernyataan if-else, switch cases, atau kondisi bersarang, unit tests itu temen terbaik lo. Ini persis skenario dimana nambah satu kondisi bisa merusak yang lain. Fitur A di cerita gue tadi? Punya setidaknya tujuh jalur kondisional berbeda. Tanpa tests, gue nggak punya cara buat memverifikasi kalau ketujuh jalur itu masih jalan setelah gue bikin perubahan.

2. Komponen yang Berhadapan Langsung dengan Pengguna

Di project Next.js gue, gue fokus banget testing Client Components karena mereka sering mengandung:

  • Logika validasi form
  • Rendering kondisional berdasarkan kondisi pengguna
  • Elemen interaktif dengan berbagai kondisi (loading, error, success)
  • Interaksi pengguna yang kompleks
// Example: Testing a complex client component
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { OrderForm } from './OrderForm';

describe('OrderForm', () => {
  it('should show validation errors for empty fields', async () => {
    render(<OrderForm />);

    const submitButton = screen.getByRole('button', { name: /submit/i });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText(/name is required/i)).toBeInTheDocument();
      expect(screen.getByText(/email is required/i)).toBeInTheDocument();
    });
  });

  it('should disable submit button during submission', async () => {
    render(<OrderForm />);

    fireEvent.change(screen.getByLabelText(/name/i), {
      target: { value: 'John Doe' }
    });
    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: '[email protected]' }
    });

    const submitButton = screen.getByRole('button', { name: /submit/i });
    fireEvent.click(submitButton);

    expect(submitButton).toBeDisabled();
  });
});

3. Utilitas Bersama dan Fungsi Pembantu

Fungsi apapun yang dipake di berbagai bagian aplikasi lo harus punya unit tests. Ini tests dengan dampak tinggi karena bug di shared utility bisa menyebar ke seluruh aplikasi lo.

4. Sebelum Refactoring Kode Lama

Berencana buat refactor fungsi lama yang ribet? Tulis tests buat perilaku saat ini dulu. Ini kasih lo kepercayaan diri kalau versi yang udah di-refactor tetap mempertahankan fungsi yang sama.

Baca Juga: Panduan Lengkap Password Validation di React dengan Chakra UI dan React Hook Form

Susunan Testing Gue Sekarang: Jest, Testing Library & jsdom

Buat project Next.js gue, gue pake kombinasi yang udah terbukti andal:

  • Jest: Test runner dan assertion library
  • React Testing Library: Buat testing komponen dengan pendekatan yang fokus ke pengguna
  • jsdom: Mensimulasikan lingkungan DOM di Node.js

Stack ini terintegrasi dengan mulus sama Next.js dan mendorong testing dari perspektif pengguna daripada detail implementasi.

// jest.config.js
const nextJest = require("next/jest");

const createJestConfig = nextJest({
    dir: "./",
});

const customJestConfig = {
    setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
    testEnvironment: "jsdom",
    moduleNameMapper: {
        "^@/components/(.*)$": "<rootDir>/components/$1",
        "^@/lib/(.*)$": "<rootDir>/lib/$1",
    },
};

module.exports = createJestConfig(customJestConfig);

Keuntungan Nyata dari Unit Testing

Ini yang berubah setelah gue berkomitmen ke unit testing:

  1. Percaya Diri dalam Perubahan: Gue bisa refactor dengan percaya diri karena tau tests bakal menangkap regresi apapun.
  2. Debugging Lebih Cepat: Ketika test gagal, langsung menunjuk dimana masalahnya.
  3. Desain Kode Lebih Baik: Nulis kode yang bisa ditest secara alami mengarah ke arsitektur lebih baik, fungsi lebih kecil, pemisahan kepentingan lebih jelas.
  4. Dokumentasi: Tests berfungsi sebagai dokumentasi hidup yang menunjukkan gimana kode harus digunakan.
  5. Lebih Sedikit Bug di Production: Menangkap masalah di development itu secara eksponensial lebih murah daripada memperbaikinya di production.

Kapan Lo Bisa Lewatin Unit Tests

Nggak semua butuh unit tests. Ini dimana gue secara sadar melewatinya:

  • Komponen presentasi sederhana tanpa logika
  • File konfigurasi
  • Fungsi getter/setter yang sepele
  • Kode yang pada dasarnya penghubung (menghubungkan unit lain yang sudah di-test)

Kuncinya adalah kesengajaan. Lewatin tests dengan sengaja, bukan karena males.

Mulai Perjalanan Testing Lo

Kalau lo di posisi gue dulu, skeptis tentang testing, ini saran gue:

  1. Mulai dari kecil: Pilih satu fungsi kompleks dan tulis tests buat itu
  2. Test kode baru: Bikin aturan kalau fitur baru harus dateng sama tests
  3. Test sebelum perbaiki bug: Ketika lo nemuin bug, tulis test yang gagal dulu, baru perbaiki
  4. Ukur cakupan: Pake tools buat lihat kode mana yang kurang tests, tapi jangan terobsesi dengan cakupan 100%
# Run tests with coverage report
npm test -- --coverage

# Run tests in watch mode during development
npm test -- --watch

Intinya

Unit testing bukan tentang menggandakan pekerjaan lo, ini tentang berinvestasi dalam kualitas dan stabilitas. Insiden production itu ngajarin gue pelajaran yang mahal: waktu yang gue “hemat” dengan melewatkan tests itu nggak ada apa-apanya dibanding waktu yang dihabiskan debugging, minta maaf ke klien, dan terburu-buru bikin perbaikan darurat.

Sekarang, ketika gue nulis kondisi kompleks atau komponen yang berhadapan dengan pengguna, gue tulis tests bersamaan dengannya. Ini udah jadi bagian dari alur kerja gue, bukan tugas terpisah. Dan jujur? Gue tidur lebih nyenyak tau kalau kode gue punya jaring pengaman.

Diri lo di masa depan, dan klien lo, bakal terima kasih banget deh.

Back to Blog

Related Posts

View All Posts »