# 2026'da React Testleri: Vitest, React Testing Library ve En İyi Uygulamalar > Vitest ve React Testing Library ile React test yazımında uzmanlaşın. Bileşen test kalıpları, asenkron işlem yönetimi, mock stratejileri ve mülakat için hazır en iyi uygulamalar. - Published: 2026-07-09 - Updated: 2026-07-09 - Author: SharpSkill - Tags: react, testing, vitest, react-testing-library - Reading time: 9 min --- 2026 yılında React testi, baskın test çalıştırıcısı olarak [Vitest](https://vitest.dev/) ve bileşen testi standardı olarak [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) (RTL) üzerine odaklanmaktadır. Bu kombinasyon hızlı geri bildirim döngüleri, yerel ES modül desteği ve uygulama detayları yerine davranışı doğrulayan testler sunar. > **Test Felsefesi** > > Kullanıcıların bileşenlerle nasıl etkileşime girdiğini yansıtan testler yazılmalıdır. Erişilebilir roller, etiketler ve metin üzerinden sorgulama yapılmalı — CSS sınıfları veya data-testid nitelikleri üzerinden değil. Bu yaklaşım gerçek hataları yakalar ve refaktörizasyona dayanıklıdır. ## React Projeleri için Vitest Yapılandırması Vitest, modern React uygulamaları için tercih edilen test çalıştırıcısı olarak Jest'in yerini aldı. Yerel ES modül desteği dönüşüm yükünü ortadan kaldırır ve Vite ile sıkı entegrasyon, yapılandırmanın geliştirme ortamını yansıtması anlamına gelir. Üretime hazır bir Vitest yapılandırması, JSX dönüşümü, yol takma adları ve test ortamı kurulumunu gereksiz şablon kod olmadan yönetir: ```typescript // vitest.config.ts import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' import tsconfigPaths from 'vite-tsconfig-paths' export default defineConfig({ plugins: [react(), tsconfigPaths()], test: { // Use jsdom for DOM APIs and React rendering environment: 'jsdom', // Run setup file before each test file setupFiles: ['./src/test/setup.ts'], // Include only test files, exclude e2e include: ['src/**/*.{test,spec}.{ts,tsx}'], // Enable global test APIs (describe, it, expect) globals: true, // Generate coverage reports coverage: { provider: 'v8', reporter: ['text', 'html', 'lcov'], exclude: ['node_modules', 'src/test/**'], }, }, }) ``` Kurulum dosyası eşleştiricileri genişletir ve testler arasında temizlik yapılandırır: ```typescript // src/test/setup.ts import '@testing-library/jest-dom/vitest' import { cleanup } from '@testing-library/react' import { afterEach, vi } from 'vitest' // Clean up rendered components after each test afterEach(() => { cleanup() }) // Mock window.matchMedia for responsive components Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockImplementation((query: string) => ({ matches: false, media: query, onchange: null, addListener: vi.fn(), removeListener: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), })), }) ``` Bu kurulum her test için temiz bir durum sağlar ve jsdom'da bulunmayan yaygın tarayıcı API mock'larını yönetir. ## React Testing Library ile Bileşen Testi React Testing Library, bileşenlerin kullanıcı perspektifinden test edilmesini zorunlu kılar. İç durumu veya prop değerlerini incelemek yerine, testler erişilebilirlik sorguları aracılığıyla render edilmiş çıktıyla etkileşime girer. Sonuçları filtreleyen ve yükleme durumlarını gösteren bir arama bileşeni örneği: ```tsx // SearchResults.tsx import { useState } from 'react' interface SearchResultsProps { onSearch: (query: string) => Promise } export function SearchResults({ onSearch }: SearchResultsProps) { const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) async function handleSearch() { if (!query.trim()) return setIsLoading(true) setError(null) try { const data = await onSearch(query) setResults(data) } catch (e) { setError('Search failed. Please try again.') } finally { setIsLoading(false) } } return (
setQuery(e.target.value)} /> {error &&

{error}

}
    {results.map((result, i) => (
  • {result}
  • ))}
) } ``` Testler, kullanıcı etkileşimleri ve asenkron durumlar boyunca bileşenin davranışını doğrular: ```typescript // SearchResults.test.tsx import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { describe, it, expect, vi } from 'vitest' import { SearchResults } from './SearchResults' describe('SearchResults', () => { it('displays results after successful search', async () => { // Arrange: mock the search function const mockSearch = vi.fn().mockResolvedValue(['React', 'Vue', 'Angular']) const user = userEvent.setup() render() // Act: type a query and click search await user.type(screen.getByLabelText('Search'), 'framework') await user.click(screen.getByRole('button', { name: 'Search' })) // Assert: results appear in the list expect(await screen.findByText('React')).toBeInTheDocument() expect(screen.getByText('Vue')).toBeInTheDocument() expect(mockSearch).toHaveBeenCalledWith('framework') }) it('shows loading state during search', async () => { // Create a promise that doesn't resolve immediately let resolveSearch: (value: string[]) => void const searchPromise = new Promise((resolve) => { resolveSearch = resolve }) const mockSearch = vi.fn().mockReturnValue(searchPromise) const user = userEvent.setup() render() await user.type(screen.getByLabelText('Search'), 'test') await user.click(screen.getByRole('button', { name: 'Search' })) // Button shows loading state expect(screen.getByRole('button', { name: 'Searching...' })).toBeDisabled() // Resolve the search to clean up resolveSearch!([]) }) it('displays error message on search failure', async () => { const mockSearch = vi.fn().mockRejectedValue(new Error('Network error')) const user = userEvent.setup() render() await user.type(screen.getByLabelText('Search'), 'query') await user.click(screen.getByRole('button', { name: 'Search' })) // Error message appears with alert role for screen readers expect(await screen.findByRole('alert')).toHaveTextContent( 'Search failed. Please try again.' ) }) }) ``` `userEvent` kütüphanesi, klavye olayları ve odak yönetimi dahil gerçekçi kullanıcı etkileşimlerini simüle eder ve sentetik olayların kaçırdığı hataları yakalar. ## Asenkron Test Kalıpları ve waitFor Asenkron işlemler açık bekleme stratejileri gerektirir. React Testing Library, `getBy` ile otomatik beklemeyi birleştiren `findBy` sorgularını ve karmaşık doğrulamalar için `waitFor` sunar. > **Sorgu Önceliği** > > Elementlerin görünmesini beklerken `waitFor` + `getBy` yerine `findBy` tercih edilmelidir. `waitFor`, durum değiştiren mevcut elementler üzerindeki doğrulamalar için ayrılmalıdır. Veri çekme hook'ları ve bileşenlerini test ederken yaygın asenkron kalıplar ortaya çıkar: ```typescript // async-patterns.test.tsx import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { describe, it, expect, vi } from 'vitest' // Wait for element to appear (preferred for new elements) it('loads user profile on mount', async () => { render() // findBy returns a promise, automatically waits up to 1000ms const heading = await screen.findByRole('heading', { name: /john doe/i }) expect(heading).toBeInTheDocument() }) // Wait for element to disappear it('removes loading spinner after data loads', async () => { render() // Wait for spinner to be removed from DOM await waitForElementToBeRemoved(() => screen.queryByRole('progressbar')) expect(screen.getByRole('table')).toBeInTheDocument() }) // Wait for multiple conditions it('enables submit button when form is valid', async () => { const user = userEvent.setup() render() const submitButton = screen.getByRole('button', { name: 'Submit' }) expect(submitButton).toBeDisabled() await user.type(screen.getByLabelText('Email'), 'user@example.com') await user.type(screen.getByLabelText('Password'), 'SecurePass123!') // waitFor retries the assertion until it passes or times out await waitFor(() => { expect(submitButton).toBeEnabled() }) }) // Avoid false positives with queryBy for absence checks it('does not show premium badge for free users', async () => { render() // queryBy returns null instead of throwing, use for absence checks expect(screen.queryByText('Premium')).not.toBeInTheDocument() }) ``` Zaman aşımı özelleştirmesi, tüm test paketini yavaşlatmadan daha yavaş işlemlere yardımcı olur: ```typescript // Increase timeout for slow operations await screen.findByText('Upload complete', {}, { timeout: 5000 }) await waitFor( () => expect(mockSubmit).toHaveBeenCalled(), { timeout: 3000, interval: 100 } ) ``` ## Mock Stratejileri: Modüller, API'ler ve Hook'lar Vitest'in mock yetenekleri, modül grafiğini kirletmeden harici bağımlılıkları yönetir. Stratejik mock'lama, bileşenleri ağ çağrılarından, üçüncü taraf kütüphanelerden ve karmaşık bağımlılıklardan izole eder. Modül mock'lama, import'ları paketleyici düzeyinde değiştirir: ```typescript // api.test.ts import { vi, describe, it, expect, beforeEach } from 'vitest' import { fetchUserData } from './api' // Mock the entire fetch module vi.mock('./http-client', () => ({ httpClient: { get: vi.fn(), }, })) import { httpClient } from './http-client' describe('fetchUserData', () => { beforeEach(() => { vi.clearAllMocks() }) it('transforms API response into user model', async () => { // Type-safe mock implementation vi.mocked(httpClient.get).mockResolvedValue({ data: { id: 1, first_name: 'John', last_name: 'Doe' }, }) const user = await fetchUserData(1) expect(user).toEqual({ id: 1, fullName: 'John Doe', }) }) }) ``` [Bağlama veya harici duruma bağlı React hook'ları](https://testing-library.com/docs/react-testing-library/api/#renderhook) için `renderHook` yardımcı programı izole test etmeyi sağlar: ```typescript // useAuth.test.ts import { renderHook, waitFor } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' import { useAuth } from './useAuth' import { AuthProvider } from './AuthContext' describe('useAuth', () => { it('returns authenticated user after login', async () => { const mockLogin = vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }) const { result } = renderHook(() => useAuth(), { wrapper: ({ children }) => ( {children} ), }) expect(result.current.user).toBeNull() expect(result.current.isAuthenticated).toBe(false) await result.current.login('alice@example.com', 'password') await waitFor(() => { expect(result.current.user).toEqual({ id: 1, name: 'Alice' }) expect(result.current.isAuthenticated).toBe(true) }) }) }) ``` MSW (Mock Service Worker), ağ düzeyinde API mock'laması sağlar ve gerçek fetch çağrılarını kullanan testleri mümkün kılar: ```typescript // handlers.ts import { http, HttpResponse } from 'msw' export const handlers = [ http.get('/api/users/:id', ({ params }) => { return HttpResponse.json({ id: params.id, name: 'Test User', }) }), http.post('/api/login', async ({ request }) => { const body = await request.json() if (body.email === 'valid@example.com') { return HttpResponse.json({ token: 'abc123' }) } return HttpResponse.json( { error: 'Invalid credentials' }, { status: 401 } ) }), ] ``` ## React 19 Server Components Testi Server Components, sunucuda çalıştığı ve HTML'i istemciye aktardığı için benzersiz test zorlukları sunar. Doğrudan birim testi, istemci bileşen testinden farklı bir yaklaşım gerektirir. Veri çekme işlemi yapan Server Components için, mock'lanmış veri kaynaklarıyla render çıktısını test etmek gerekir: ```typescript // ServerComponent.test.tsx import { describe, it, expect, vi } from 'vitest' // Mock the data fetching function vi.mock('./db', () => ({ getUser: vi.fn().mockResolvedValue({ id: 1, name: 'Server User' }), })) import { render, screen } from '@testing-library/react' import { UserProfile } from './UserProfile.server' describe('UserProfile Server Component', () => { it('renders user data from database', async () => { // Server components are async, await the component const Component = await UserProfile({ userId: '1' }) render(Component) expect(screen.getByRole('heading')).toHaveTextContent('Server User') }) }) ``` [Playwright](https://playwright.dev/) ile entegrasyon testleri, tam render pipeline'ını test ederek Server Components için en güvenilir kapsamı sağlar: ```typescript // e2e/server-component.spec.ts import { test, expect } from '@playwright/test' test('server component renders with streamed data', async ({ page }) => { await page.goto('/users/1') // Wait for streaming to complete await expect(page.getByRole('heading', { name: /profile/i })).toBeVisible() await expect(page.getByText('Server User')).toBeVisible() }) ``` ## Karmaşık Durumlu Özel Hook'ların Testi Karmaşık durum, yan etkiler veya harici abonelikler yöneten özel hook'lar, ayrılmış testlerden fayda sağlar. React Testing Library'den `renderHook` yardımcı programı, hook yaşam döngüsünü ve güncellemeleri yönetir. Bir API ile koordine olan debounced arama hook'u örneği: ```typescript // useDebounceSearch.ts import { useState, useEffect, useCallback } from 'react' export function useDebounceSearch( searchFn: (query: string) => Promise, delay = 300 ) { const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [isLoading, setIsLoading] = useState(false) useEffect(() => { if (!query) { setResults([]) return } setIsLoading(true) const timeoutId = setTimeout(async () => { try { const data = await searchFn(query) setResults(data) } finally { setIsLoading(false) } }, delay) return () => clearTimeout(timeoutId) }, [query, searchFn, delay]) return { query, setQuery, results, isLoading } } ``` Testler debounce davranışını ve durum geçişlerini doğrular: ```typescript // useDebounceSearch.test.ts import { renderHook, act, waitFor } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { useDebounceSearch } from './useDebounceSearch' describe('useDebounceSearch', () => { beforeEach(() => { vi.useFakeTimers() }) it('debounces search calls', async () => { const mockSearch = vi.fn().mockResolvedValue(['result1', 'result2']) const { result } = renderHook(() => useDebounceSearch(mockSearch, 300)) // Type multiple characters quickly act(() => result.current.setQuery('r')) act(() => result.current.setQuery('re')) act(() => result.current.setQuery('rea')) act(() => result.current.setQuery('reac')) act(() => result.current.setQuery('react')) // Search not called yet (debouncing) expect(mockSearch).not.toHaveBeenCalled() // Advance timers past debounce delay await act(async () => { vi.advanceTimersByTime(300) }) // Only one search call with final query expect(mockSearch).toHaveBeenCalledTimes(1) expect(mockSearch).toHaveBeenCalledWith('react') }) it('shows loading state during search', async () => { let resolveSearch: (value: string[]) => void const mockSearch = vi.fn().mockImplementation( () => new Promise((resolve) => { resolveSearch = resolve }) ) const { result } = renderHook(() => useDebounceSearch(mockSearch, 100)) act(() => result.current.setQuery('test')) await act(async () => { vi.advanceTimersByTime(100) }) expect(result.current.isLoading).toBe(true) await act(async () => { resolveSearch(['result']) }) expect(result.current.isLoading).toBe(false) expect(result.current.results).toEqual(['result']) }) }) ``` Vitest'ten sahte zamanlayıcılar, gerçek gecikmeler olmadan zamana bağlı davranışların deterministik testini sağlar. ## React Testi Hakkında Mülakat Soruları Teknik mülakatlar sıklıkla test bilgisini sorgular. Bu sorular teorik kavramlar yerine pratik anlayışı değerlendirir. > **Yaygın Tuzak** > > Durum değerleri veya bileşen yöntemleri gibi uygulama detaylarını test etmekten kaçınılmalıdır. İç yapı refaktörize edildiğinde bozulan testler yanlış güven ve bakım yükü sağlar. **React Testing Library'de sorgu önceliği** Önerilen sorgu önceliği erişilebilirliği takip eder: `getByRole` > `getByLabelText` > `getByPlaceholderText` > `getByText` > `getByTestId`. Rol tabanlı sorgular bileşenlerin erişilebilir kalmasını sağlar ve testler işaretleme değişikliklerine dayanır. **`findBy` vs `waitFor` ne zaman kullanılmalı** `findBy` sorguları DOM'da elementlerin görünmesini bekler ve bir promise döndürür. Asenkron işlemlerden sonra yeni elementler beklenirken kullanılmalıdır. `waitFor` hemen geçemeyebilecek doğrulamaları sarar, başarı veya zaman aşımına kadar yeniden dener. Mevcut elementlerdeki durum değişikliklerini doğrularken `waitFor` kullanılmalıdır. **Fetch mock'lama vs MSW** Doğrudan fetch mock'lama (`vi.spyOn(global, 'fetch')`) basit durumlar için çalışır ancak Response nesnelerinin ve hata yönetiminin yeniden uygulanmasını gerektirir. MSW ağ düzeyinde yakalar, testlerin gerçek fetch çağrılarını, istek serileştirmesini ve hata yönetimi kod yollarını kullanmasına izin verir. Bu kavramlarla daha derin pratik için SharpSkill'deki [React Testing mülakat sorularını](/technologies/react-next/interview-questions/react-testing) inceleyebilirsiniz. ## Sonuç - Vitest'i jsdom ortamı, eşleştirici uzantıları için kurulum dosyaları ve test kapsam boşluklarında görünürlük için kapsam raporlaması ile yapılandırın - Bileşenleri erişilebilir roller ve etiketlerle sorgulayarak kullanıcıya yönelik davranışı doğrulayan ve gerçek hataları yakalayan testler yazın - Asenkron işlemlerden sonra görünen elementler için `findBy` sorgularını, değişen durum üzerindeki doğrulamalar için `waitFor` kullanın - Uygun düzeyde mock'layın: birim izolasyonu için modül mock'ları, gerçekçi ağ katmanı testi için MSW - Karmaşık durum yönetimini ve yan etkileri doğrulamak için `renderHook` ve sahte zamanlayıcılarla özel hook'ları test edin - Server Components, testlerde asenkron render veya Playwright ile tam entegrasyon testleri gerektirir - İlgili okuma: Tip güvenli test kalıpları için [Gelişmiş React Hooks kalıpları](/blog/react-next/advanced-react-hooks-patterns-optimizations) ve [React ile TypeScript](/technologies/react-next/interview-questions/typescript-react) --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/react-next/react-testing-2026-vitest-rtl-best-practices