Yazılarımız

Veri Akademi

REACT TESTİNG: COMPONENT TEST STRATEJİSİ, MOCK YAKLAŞIMI VE SAĞLAM CI

React uygulamalarında “test yazıyoruz” demek kolay; zor olan, testlerin güvenilir, bakımı ucuz ve CI’da stabil çalışır hâle gelmesi. Component testleri özellikle hızlı geri bildirim verdiği için cazip görünür; ama yanlış mock’lar, kırılgan selector’lar ve gerçek kullanıcı akışını taklit etmeyen senaryolar bir süre sonra güveni eritir.

Bu yazıda React bileşen testlerinde sağlam bir strateji kurmayı, mock yaklaşımını bilinçli seçmeyi ve CI hattında testleri “her çalıştırmada aynı sonucu veren” bir seviyeye taşımayı hedefliyoruz. Amaç, sadece test sayısını artırmak değil; ürün davranışını doğrulayan, refactor’a dirençli ve ekipçe sürdürülebilir bir test kültürü oluşturmak.

Odak noktamız component test stratejisi. Bunun yanında, entegrasyon sınırlarını netleştirme, API mock’lama (MSW), flaky testleri azaltma, coverage hedefleri ve CI optimizasyonu gibi kritik parçaları da uçtan uca bağlayacağız.

Bileşen sınırları, kullanıcı akışları ve test katmanlarını aynı düzlemde dengeleyen bir yaklaşım haritası

Primary Keyword: React component test stratejisi ile hedefi netleştirme

“React component test stratejisi” derken önce şu soruyu yanıtlayın: Testler hangi riski azaltacak? UI davranışını mı, iş kuralını mı, ağ hatalarını mı, erişilebilirlik uyumunu mu? Her şeyi component testine yığmak kısa vadede hızlı görünür; uzun vadede testlerin amaçları karışır ve bakım maliyeti artar.

Sağlam bir strateji üç temel prensipten ilerler: (1) Kullanıcı değerini doğrulamak, (2) Uygulama sınırlarını doğru yerde kesmek, (3) Mock’ları “gerçeğe benzer” ama “bağımlılığı izole eden” şekilde kurgulamak. Bu üçü dengelenmezse testler ya çok yüzeysel kalır ya da gerçekçi olmaktan çıkar.

Test kapsamını davranış üzerinden tanımla

Component testlerinde hedef, DOM’un her detayını doğrulamak değil; kullanıcı için anlamlı çıktıları doğrulamaktır. Örneğin “butonun class’ı” yerine “butona basınca yükleme durumu görünür ve kaydetme tamamlanınca başarı mesajı çıkar” gibi davranışları test etmek daha dayanıklıdır.

Sınırları belirle: UI, domain ve altyapı

UI bileşenleri (render, etkileşim), domain katmanı (iş kuralları) ve altyapı (API, cache, analytics) farklı hızlarda değişir. Component testini UI davranışı için kullanın; domain’i mümkünse saf fonksiyonlarla unit test edin; altyapıyı ise MSW gibi araçlarla kontrollü biçimde simüle edin.


Test piramidi ve React’te katmanlandırma

React ekosisteminde klasik “unit/integration/e2e” ayrımı çoğu zaman bulanıklaşır. React Testing Library ile yazılan testler, çoğunlukla kullanıcı etkileşimi üzerinden çalıştığı için “component integration” olarak da görülebilir. Önemli olan isim değil; katmanın maliyeti ve sağladığı güven.

Pratik bir dağılım: az sayıda kritik E2E, orta sayıda entegrasyon niteliğinde component testi, çok sayıda hızlı unit test. Bu dağılım, CI süresini makul tutarken üretim hatalarını yakalama olasılığını artırır.

Ne zaman unit test, ne zaman component test?

Bir parça UI’dan bağımsız çalışabiliyorsa (ör. fiyat hesaplama, tarih aralığı doğrulama), unit test ile çok hızlı ve net doğrulanır. UI davranışı (modal açılması, form validasyonu, hata mesajı) için component test daha doğru bir maliyet/fayda sunar.

Golden rule: Implementation detail’a değil, kullanıcı çıktısına bak

Hook’ların kaç kez çağrıldığı, state’in hangi sırayla güncellendiği gibi detaylar refactor’da sık değişir. Testleriniz “kullanıcı ne görür ve ne yapabilir” sorusuna odaklanırsa hem sürdürülebilir olur hem de tasarım değişikliklerinde gereksiz kırılmaz.

React Testing Library ile bileşen testi tasarımı

React Testing Library’nin merkezinde “kullanıcıya yakın sorgular” vardır. getByRole, getByLabelText ve findByText gibi sorgular, erişilebilirlik ağacına ve görünen metne yaslandığı için daha güvenilir olur. Bu yaklaşım, testleri ürün davranışına yaklaştırır.

Selector seçimi: role, label ve text önceliği

Testlerinizi data-testid ile kurtarmak mümkün; ama bunu varsayılan yaklaşım yaparsanız UI yeniden düzenlemelerinde testler davranışı değil, DOM’u doğrular hâle gelir. Önce role/label/text ile deneyin; sadece gerekli yerlerde testid kullanın.

Asenkron akışlar: findBy ve waitFor’u bilinçli kullan

API sonrası güncellenen UI’larda findBy* sorguları genelde en temiz çözümdür; çünkü DOM’un beklenen hâle gelmesini doğal biçimde bekler. waitFor ise daha genel bir mekanizmadır; koşulu açıkça tanımladığınızda güçlüdür ama fazla kullanılırsa test niyetini gölgeler.

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ProfileForm } from './ProfileForm';

test('kullanıcı formu kaydedince başarı mesajı gösterir', async () => {
  const user = userEvent.setup();
  render(<ProfileForm />);

  await user.type(screen.getByLabelText(/ad soyad/i), 'Ada Lovelace');
  await user.click(screen.getByRole('button', { name: /kaydet/i }));

  expect(await screen.findByText(/kaydedildi/i)).toBeInTheDocument();
});

Mock yaklaşımı: neyi, ne kadar ve hangi seviyede taklit edeceğiz?

Mock’ların amacı bağımlılıkları kesmek ve deterministik testler yazmaktır. Ancak her şeyi mock’lamak, testleri “uydurma bir evrende” çalıştırır ve üretimdeki entegrasyon hatalarını kaçırmanıza yol açar. Doğru yaklaşım, mock seviyesini bağımlılığın türüne göre seçmektir.

Fonksiyon/servis mock’u vs ağ katmanı mock’u

Basit bağımlılıklar (ör. analytics event’i) için fonksiyon mock’u yeterlidir. API gibi davranışsal bağımlılıklar için ise ağ seviyesinde mock daha gerçekçidir. MSW (Mock Service Worker), request/response akışını taklit ederek UI’nun gerçek hayata yakın koşullarda çalışmasını sağlar.

Ne zaman “gerçek” ile test etmek daha iyi?

Örneğin bir tasarım sisteminden gelen UI bileşeni çok stabil ve iyi testlenmişse, onu mock’lamak yerine gerçek kullanmak daha az riskli olabilir. Buna karşılık, dış servislere giden çağrılar veya tarih/saat gibi deterministik olmayan kaynaklar kontrollü hâle getirilmelidir.

  • Tarih/saat: sabitle (fake timers) veya clock abstraction kullan.
  • Ağ istekleri: MSW ile endpoint davranışını modelle.
  • Global API (localStorage, matchMedia): ince mock’larla kapsa.
  • Randomness: seed’li üretim veya sabit değer.

MSW ile API mock: component testlerinde gerçekçi entegrasyon hissi

MSW, test ortamında browser fetch/XHR seviyesinde istekleri yakalar ve belirlediğiniz handler’lar ile yanıt döner. Böylece component testiniz, “servisi gerçekten çağırıyormuş” gibi davranır; ama dış bağımlılıktan kopuk olduğu için hızlı ve deterministiktir. Bu, özellikle form akışları, hata durumları ve yükleme senaryolarında büyük fark yaratır.

Başarılı yanıt ve hata senaryosunu aynı test setinde ele alma

İyi bir set, sadece “happy path”i değil; 400/500 hatalarını, boş veri dönüşlerini ve gecikmeyi de kapsar. Buradaki amaç her detayı test etmek değil, kullanıcıya yansıyan kritik davranışları doğrulamaktır.

import { setupServer } from 'msw/node';
import { http, HttpResponse, delay } from 'msw';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { NotificationsPanel } from './NotificationsPanel';

const server = setupServer(
  http.get('/api/notifications', async () => {
    await delay(150);
    return HttpResponse.json([{ id: 1, title: 'Build tamamlandı' }]);
  })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('yükleme sonrası bildirimleri listeler', async () => {
  render(<NotificationsPanel />);
  expect(screen.getByText(/yükleniyor/i)).toBeInTheDocument();
  expect(await screen.findByText(/build tamamlandı/i)).toBeInTheDocument();
});

test('sunucu hatasında kullanıcıya anlamlı mesaj verir', async () => {
  server.use(
    http.get('/api/notifications', () => HttpResponse.json({ message: 'fail' }, { status: 500 }))
  );

  render(<NotificationsPanel />);
  expect(await screen.findByText(/bir şeyler ters gitti/i)).toBeInTheDocument();
});

Flaky testleri azaltma: deterministik kurulum ve stabil assertions

Flaky test, aynı kod değişmeden bazen geçip bazen kalan testtir ve CI güvenini hızla bitirir. Genellikle zamanlama, paylaşılan global state, gerçek ağ çağrıları veya yanlış asenkron bekleme kalıplarından kaynaklanır. Bu noktada “testleri daha uzun timeout’la geçirmeye çalışma” yaklaşımı sorunu gizler; çözmez.

En sık kaynaklar ve hızlı önlemler

Asenkron UI güncellemelerinde findBy sorguları ve net koşullarla waitFor kullanımı kritik. Test ortamında rastgelelik, tarih, timezone gibi değişkenleri sabitlemek de stabiliteyi artırır. Ayrıca testler arası sızıntıyı engellemek için her testten sonra mock’ları resetlemek gerekir.

Render helper ve tekil test setup

Uygulama provider’ları (router, query client, i18n) her testte farklı şekillerde kurulursa, davranış farkları oluşabilir. Basit bir render helper ile tek bir standart kurulum sağlamak, kırılganlığı azaltır ve test okumayı kolaylaştırır.

Coverage hedefleri ve anlamlı metrikler

Coverage tek başına kalite göstergesi değildir; ama “hiç test yok” ile “kritik yollar kapsandı” arasındaki farkı görünür kılar. İyi yaklaşım, yüzde hedefi koymadan önce kritik akışları ve risk alanlarını belirlemektir. Örneğin ödeme, kimlik doğrulama, yetkilendirme ve veri kaybı riskleri daha yüksek öncelik taşır.

Satır kapsamı yerine risk kapsamı

“%90 satır kapsamı” hedefi uğruna anlamsız assertion’lar üretmek, test maliyetini artırır. Bunun yerine, kullanıcı için önemli senaryoların kapsandığına dair bir kontrol listesi tutmak daha değerlidir. Coverage’ı da bu listeyi destekleyen bir sinyal olarak kullanın.

Erişilebilirlik kontrolleri ile kaliteyi yükselt

Role/label tabanlı sorgular zaten erişilebilirlik disiplinini destekler. Buna ek olarak, temel erişilebilirlik kurallarını otomatikleştirmek (ör. hatalı aria kullanımını yakalamak) üretim kalitesini artırır ve UI refactor’larında geri kaymayı azaltır.

Sağlam CI: hızlı, tekrar edilebilir ve güven veren pipeline

CI’da amaç yalnızca testleri koşturmak değil; sonuçların tekrar edilebilir olmasını sağlamak, paralelleştirme ile süreyi düşürmek ve raporlamayı netleştirmektir. Bağımlılıkların kilitlenmesi, cache stratejisi, test splitting ve stabil ortam değişkenleri burada kritik rol oynar.

Node sürümü, lockfile ve cache politikası

CI’da farklı Node sürümleri veya güncel olmayan lockfile yüzünden test davranışı değişebilir. Tek sürüm standardı, lockfile’ın repo’da güncel olması ve package manager cache’inin doğru kullanımı genellikle ilk büyük kazanımdır.

Testleri şeffaf raporla ve kırılmayı hızlı teşhis et

Test raporları, coverage çıktıları ve hata logları bulunmazsa, kırılan pipeline ekipte “kim bakacak” sorusuna dönüşür. Basit bir rapor standardı belirleyin. Fail olduğunda test adı, hata mesajı, ilgili screenshot/video (E2E’de) ve koşan komut net görünmeli.

Bu noktada pratik bir başlangıç için eğitim içeriğine göz atmak isterseniz: React Eğitimi.

CI hattında test raporları, cache adımları ve paralel çalışan görevlerle güveni artıran bir düzen

Uygulanabilir kontrol listesi: ekibinle standartlaştır

Strateji, dokümana yazılınca değil; ekip içinde tekrar tekrar uygulandığında işe yarar. Aşağıdaki kısa listeyi repo’da bir rehber olarak tutmak, PR incelemelerinde ortak bir dil oluşturur ve kaliteyi zamana yayar.

  1. Test niyeti: Davranışı mı doğruluyor, implementasyonu mu?
  2. Sorgu seçimi: role/label/text öncelikli mi, testid gerekçeli mi?
  3. Mock seviyesi: Gereğinden fazla mock var mı, MSW uygun mu?
  4. Asenkronite: findBy/waitFor doğru yerde mi, timeout şişirme var mı?
  5. Deterministik ortam: tarih, random, locale sabit mi?
  6. CI uyumu: aynı komut yerelde ve CI’da aynı sonucu veriyor mu?

Refactor dostu testler için son öneri

Testleriniz refactor’da sürekli kırılıyorsa, çoğu zaman DOM’un ayrıntılarına veya iç implementasyona fazla yaklaşıyordur. Daha üst seviyede, kullanıcı etkisini doğrulayın. Bu yaklaşım, bakımı düşük test setlerinin en güçlü ortak noktasıdır.

Özet: güveni üreten sistem

React’te iyi test, araçtan çok “tasarım kararıdır”. Component test stratejisi; doğru katmanlandırma, bilinçli mock yaklaşımı, MSW ile gerçekçi entegrasyon ve CI disiplininin birleşimidir. Bu bütünlük kurulduğunda testler, yalnızca hata yakalamaz; geliştirme hızını ve ekip güvenini de yükseltir.

 VERİ AKADEMİ