İçeriğe geç
Sedat Demir
Geri dön

Vitest v4: Jest'in Yerini Alan Modern React Test Standardı

Vitest v4: Jest'in Yerini Alan Modern React Test Standardı

React ekosisteminde test yazma kültürü yıllardır Jest etrafında şekillendi. Ancak Vite'ın build tool olarak baskın hale gelmesiyle birlikte, Jest'in konfigürasyon karmaşıklığı, ESM uyumsuzlukları ve yavaş çalışma süreleri geliştiricileri alternatif aramaya itti. İşte tam bu noktada Vitest sahneye çıktı ve v4 sürümüyle birlikte artık "alternatif" değil, yeni standart olma yolunda emin adımlarla ilerliyor.


Jest Neden Geride Kalıyor?

Jest, Facebook tarafından geliştirilen ve uzun yıllar boyunca React projelerinin varsayılan test framework'ü olan güçlü bir araç. Ancak modern JavaScript ekosisteminin hızlı evrimi, Jest'in bazı temel limitlerini gün yüzüne çıkardı:


Vitest Nedir ve Neden v4 Önemli?

Vitest, Vite'ın dev server altyapısı üzerine inşa edilmiş bir test framework'üdür. Vite'ın HMR (Hot Module Replacement) hızını ve native ESM desteğini test ortamına taşır. Jest-uyumlu bir API sunarak geçiş sürecini kolaylaştırır.

Vitest v4'ün Öne Çıkan Özellikleri

  1. Project-Aware Test Isolation: v4 ile her test dosyası proje bağlamında izole edilir, ancak modül önbelleği akıllıca paylaşılır. Bu, izolasyondan ödün vermeden hız kazandırır.
  2. Gelişmiş Browser Mode: @vitest/browser paketi artık Playwright ve WebDriverIO ile tam entegre çalışır. Gerçek tarayıcıda test koşmak hiç bu kadar kolay olmamıştı.
  3. Inline Workspace Desteği: Monorepo projelerinde her paketin kendi test konfigürasyonunu vitest.workspace.ts üzerinden yönetmek artık çok daha sezgisel.
  4. Type-Safe Mocking: vi.mock() ve vi.spyOn() artık TypeScript ile tam uyumlu tip çıkarımı yapabiliyor.
  5. Snapshot Geliştirmeleri: Inline snapshot'lar ve dosya tabanlı snapshot'lar arasında geçiş daha esnek hale geldi.
  6. Performans İyileştirmeleri: Önceki sürümlere kıyasla %30'a varan hız artışı, özellikle büyük projelerde belirgin fark yaratıyor.

Vitest v4 Kurulumu ve Temel Konfigürasyon

Bir React + Vite projesinde Vitest kurmak son derece basittir:

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom

vite.config.ts dosyanızı güncelleyin — ayrı bir config dosyasına bile ihtiyacınız yok:

/// <reference types="vitest/config" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
    include: ['src/**/*.{test,spec}.{ts,tsx}'],
    css: true,
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: ['node_modules/', 'src/test/'],
    },
  },
});

Setup dosyanız (src/test/setup.ts):

import '@testing-library/jest-dom/vitest';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';

afterEach(() => {
  cleanup();
});

package.json'a script ekleyin:

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

Not: vitest komutu varsayılan olarak watch modunda çalışır. CI ortamlarında vitest run kullanmanız yeterlidir.


React Bileşenlerini Vitest ile Test Etmek

Basit Bir Bileşen Testi

Aşağıdaki basit bir Counter bileşenini ele alalım:

// src/components/Counter.tsx
import { useState } from 'react';

interface CounterProps {
  initialCount?: number;
}

export function Counter({ initialCount = 0 }: CounterProps) {
  const [count, setCount] = useState(initialCount);

  return (
    <div>
      <p data-testid="count-display">Sayaç: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>Artır</button>
      <button onClick={() => setCount((c) => c - 1)}>Azalt</button>
      <button onClick={() => setCount(0)}>Sıfırla</button>
    </div>
  );
}

Şimdi bu bileşen için kapsamlı bir test dosyası yazalım:

// src/components/Counter.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect } from 'vitest';
import { Counter } from './Counter';

describe('Counter Bileşeni', () => {
  it('varsayılan değerle render edilmeli', () => {
    render(<Counter />);
    expect(screen.getByTestId('count-display')).toHaveTextContent('Sayaç: 0');
  });

  it('initialCount prop ile başlamalı', () => {
    render(<Counter initialCount={10} />);
    expect(screen.getByTestId('count-display')).toHaveTextContent('Sayaç: 10');
  });

  it('Artır butonuna tıklanınca sayaç artmalı', async () => {
    const user = userEvent.setup();
    render(<Counter />);

    await user.click(screen.getByText('Artır'));
    expect(screen.getByTestId('count-display')).toHaveTextContent('Sayaç: 1');

    await user.click(screen.getByText('Artır'));
    expect(screen.getByTestId('count-display')).toHaveTextContent('Sayaç: 2');
  });

  it('Sıfırla butonu sayacı sıfırlamalı', async () => {
    const user = userEvent.setup();
    render(<Counter initialCount={5} />);

    await user.click(screen.getByText('Sıfırla'));
    expect(screen.getByTestId('count-display')).toHaveTextContent('Sayaç: 0');
  });
});

API Çağrılarını Mock'lamak

Modern React uygulamalarında API çağrıları kaçınılmazdır. Vitest'in vi.mock() ve vi.fn() API'leri Jest ile neredeyse birebir uyumludur:

// src/hooks/useUsers.ts
import { useState, useEffect } from 'react';

interface User {
  id: number;
  name: string;
  email: string;
}

export function useUsers() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    fetch('/api/users')
      .then((res) => {
        if (!res.ok) throw new Error('Kullanıcılar yüklenemedi');
        return res.json();
      })
      .then(setUsers)
      .catch((err) => setError(err.message))
      .finally(() => setLoading(false));
  }, []);

  return { users, loading, error };
}
// src/hooks/useUsers.test.ts
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useUsers } from './useUsers';

const mockUsers = [
  { id: 1, name: 'Ahmet Yılmaz', email: 'ahmet@example.com' },
  { id: 2, name: 'Elif Kaya', email: 'elif@example.com' },
];

beforeEach(() => {
  vi.restoreAllMocks();
});

describe('useUsers Hook', () => {
  it('kullanıcıları başarıyla yüklemeli', async () => {
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: true,
        json: () => Promise.resolve(mockUsers),
      })
    );

    const { result } = renderHook(() => useUsers());

    expect(result.current.loading).toBe(true);

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.users).toEqual(mockUsers);
    expect(result.current.error).toBeNull();
  });

  it('hata durumunu yönetmeli', async () => {
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: false,
        status: 500,
      })
    );

    const { result } = renderHook(() => useUsers());

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.error).toBe('Kullanıcılar yüklenemedi');
    expect(result.current.users).toEqual([]);
  });
});

Jest'ten Vitest'e Geçiş Rehberi

Mevcut bir Jest projesini Vitest'e taşımak düşündüğünüzden daha kolaydır. İşte adım adım yapmanız gerekenler:

1. Bağımlılıkları Değiştirin

# Jest bağımlılıklarını kaldırın
npm uninstall jest ts-jest babel-jest @types/jest jest-environment-jsdom

# Vitest bağımlılıklarını ekleyin
npm install -D vitest @vitest/coverage-v8 @vitest/ui

2. Import'ları Güncelleyin

Jest'te global olarak kullanılan describe, it, expect gibi fonksiyonları Vitest'te ya global yapılandırmayla ya da import ile kullanabilirsiniz:

// Jest (implicit globals)
describe('test', () => { ... });

// Vitest (explicit imports — önerilen)
import { describe, it, expect, vi } from 'vitest';
describe('test', () => { ... });

3. Mock Sözdizimi Farklılıkları

Jest Vitest
jest.fn() vi.fn()
jest.mock() vi.mock()
jest.spyOn() vi.spyOn()
jest.useFakeTimers() vi.useFakeTimers()
jest.requireActual() vi.importActual()

4. Otomatik Dönüştürme Aracı

Vitest ekibi, Jest testlerini otomatik olarak dönüştüren bir codemod aracı sunar:

npx @vitest/codemod migrate-jest ./src

Bu komut, tüm jest.* çağrılarını vi.* ile değiştirir ve gerekli import'ları ekler.


Vitest UI: Testlerinizi Görsel Olarak Yönetin

Vitest'in en çarpıcı özelliklerinden biri yerleşik UI dashboard'dur:

npx vitest --ui

Bu komut tarayıcınızda interaktif bir arayüz açar. Bu arayüzde:


Performans Karşılaştırması: Vitest v4 vs Jest

Orta ölçekli bir React projesinde (yaklaşık 500 test dosyası) yapılan karşılaştırma sonuçları:

Metrik Jest 29 Vitest v4
Cold start süresi ~12s ~3s
Watch mode yeniden çalışma ~4s ~0.5s
Toplam test suite süresi ~45s ~18s
Bellek kullanımı ~850MB ~420MB
ESM desteği Deneysel Tam destek

Bu rakamlar projeye göre değişkenlik gösterebilir, ancak genel eğilim Vitest lehine açık bir fark ortaya koymaktadır.


İleri Düzey Özellikler

In-Source Testing

Vitest, test kodunu doğrudan kaynak dosyanın içine yazmanıza olanak tanır. Bu özellik özellikle utility fonksiyonları için idealdir:

// src/utils/math.ts
export function fibonacci(n: number): number {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

if (import.meta.vitest) {
  const { describe, it, expect } = import.meta.vitest;

  describe('fibonacci', () => {
    it('temel değerleri doğru hesaplamalı', () => {
      expect(fibonacci(0)).toBe(0);
      expect(fibonacci(1)).toBe(1);
      expect(fibonacci(6)).toBe(8);
      expect(fibonacci(10)).toBe(55);
    });
  });
}

Bu yaklaşımı etkinleştirmek için konfigürasyona şunu ekleyin:

// vite.config.ts
export default defineConfig({
  test: {
    includeSource: ['src/**/*.ts'],
  },
  define: {
    'import.meta.vitest': 'undefined', // Production build'de test kodu dahil edilmez
  },
});

Type Testing

Vitest v4 ile TypeScript tiplerini de test edebilirsiniz:

// src/types.test-d.ts
import { expectTypeOf, describe, it } from 'vitest';

describe('Type Tests', () => {
  it('string dizisi doğru tip olmalı', () => {
    const names = ['Ahmet', 'Mehmet'];
    expectTypeOf(names).toEqualTypeOf<string[]>();
  });
});
npx vitest typecheck

Sonuç

Vitest v4, React test ekosisteminde bir paradigma değişiminin somut ifadesidir. Jest'in sunduğu olgun ve kapsamlı API'yi korurken, Vite'ın hız ve modern JavaScript desteğiyle birleştirerek geliştiricilere üstün bir deneyim sunmaktadır.

Özetlemek gerekirse:

Eğer hâlâ Jest kullanıyorsanız ve projeniz Vite tabanlıysa, Vitest'e geçiş artık bir tercih değil, bir zorunluluk haline gelmiştir. Yeni bir React projesine başlıyorsanız ise Vitest v4, test altyapınız için en doğru seçim olacaktır.

Testleriniz hızlı, kodunuz güvenli olsun! 🚀


Share this post on:

Sonraki Yazı
shadcn/ui Nasıl Endüstri Standardı Oldu? 110K Yıldızlı Bileşen Devriminin Anatomisi