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ı:
- ESM (ES Modules) desteği hâlâ deneysel: Native ESM kullanmak isteyen projelerde
--experimental-vm-modulesflag'ine ihtiyaç duyulması ve çeşitli uyumsuzluklar ciddi sorun oluşturuyor. - Konfigürasyon karmaşıklığı:
babel-jest,ts-jest,jest.config.jsdosyaları ve transform ayarlarıyla boğuşmak modern bir DX (Developer Experience) beklentisinin çok gerisinde. - Yavaş cold start süreleri: Jest her test dosyası için ayrı bir worker başlatır ve büyük projelerde bu süre katlanarak artar.
- Vite ile doğal entegrasyon eksikliği: Proje Vite kullanıyorsa, Jest'in ayrı bir bundler/transformer pipeline'ı kurması gerekir; bu da çift konfigürasyon anlamına gelir.
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
- 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.
- Gelişmiş Browser Mode:
@vitest/browserpaketi artık Playwright ve WebDriverIO ile tam entegre çalışır. Gerçek tarayıcıda test koşmak hiç bu kadar kolay olmamıştı. - Inline Workspace Desteği: Monorepo projelerinde her paketin kendi test konfigürasyonunu
vitest.workspace.tsüzerinden yönetmek artık çok daha sezgisel. - Type-Safe Mocking:
vi.mock()vevi.spyOn()artık TypeScript ile tam uyumlu tip çıkarımı yapabiliyor. - Snapshot Geliştirmeleri: Inline snapshot'lar ve dosya tabanlı snapshot'lar arasında geçiş daha esnek hale geldi.
- 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 jsdomvite.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:
vitestkomutu varsayılan olarak watch modunda çalışır. CI ortamlarındavitest runkullanmanı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/ui2. 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 ./srcBu 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 --uiBu komut tarayıcınızda interaktif bir arayüz açar. Bu arayüzde:
- Test sonuçlarını gerçek zamanlı izleyebilir,
- Her testin çalışma süresini görebilir,
- Başarısız testlerin detaylı hata mesajlarını inceleyebilir,
- Modül grafiğini görselleştirebilir,
- Filtreleme ve arama yapabilirsiniz.
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 typecheckSonuç
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:
- Hız: Vite'ın HMR altyapısı sayesinde testler çok daha hızlı çalışır.
- Sıfır konfigürasyon: Vite projelerinde ek bir yapılandırmaya neredeyse hiç gerek yoktur.
- Jest uyumluluğu: Mevcut Jest testlerinizi minimum değişiklikle taşıyabilirsiniz.
- Modern JavaScript: ESM, TypeScript ve JSX için native destek sunar.
- Geliştirici deneyimi: UI dashboard, in-source testing ve type testing gibi yenilikçi özellikler sunar.
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! 🚀