Türkçe

Daha güvenilir ve sürdürülebilir yazılımlar oluşturmak için gelişmiş Jest test kalıplarında ustalaşın. Mocking, snapshot testleri, özel eşleştiriciler gibi teknikleri ve daha fazlasını küresel geliştirme ekipleri için keşfedin.

Jest: Sağlam Yazılımlar için Gelişmiş Test Kalıpları

Günümüzün hızlı tempolu yazılım geliştirme dünyasında, kod tabanınızın güvenilirliğini ve kararlılığını sağlamak esastır. Jest, JavaScript testleri için fiili bir standart haline gelmiş olsa da, temel birim testlerinin ötesine geçmek, uygulamalarınıza olan güveni yeni bir seviyeye taşır. Bu yazı, küresel bir geliştirici kitlesine hitap ederek, sağlam yazılımlar oluşturmak için gerekli olan gelişmiş Jest test kalıplarını derinlemesine incelemektedir.

Neden Temel Birim Testlerinin Ötesine Geçmeliyiz?

Temel birim testleri, tek tek bileşenleri izole bir şekilde doğrular. Ancak, gerçek dünya uygulamaları bileşenlerin etkileşimde bulunduğu karmaşık sistemlerdir. Gelişmiş test kalıpları, bu karmaşıklıkları ele alarak bize şunları sağlar:

Mocking ve Spy'larda Uzmanlaşma

Mocking, test edilen birimin bağımlılıklarını kontrol edilen sahte nesnelerle değiştirerek onu izole etmek için çok önemlidir. Jest bunun için güçlü araçlar sunar:

jest.fn(): Mock'ların ve Spy'ların Temeli

jest.fn() bir mock fonksiyonu oluşturur. Çağrılarını, argümanlarını ve geri dönüş değerlerini takip edebilirsiniz. Bu, daha karmaşık mocking stratejilerinin temel yapı taşıdır.

Örnek: Fonksiyon Çağrılarının Takibi

// component.js
export const fetchData = () => {
  // Bir API çağrısını simüle eder
  return Promise.resolve({ data: 'some data' });
};

export const processData = async (fetcher) => {
  const result = await fetcher();
  return `İşlendi: ${result.data}`;
};

// component.test.js
import { processData } from './component';

test('verileri doğru şekilde işlemelidir', async () => {
  const mockFetcher = jest.fn().mockResolvedValue({ data: 'mocked data' });
  const result = await processData(mockFetcher);
  expect(result).toBe('İşlendi: mocked data');
  expect(mockFetcher).toHaveBeenCalledTimes(1);
  expect(mockFetcher).toHaveBeenCalledWith();
});

jest.spyOn(): Değiştirmeden Gözlemleme

jest.spyOn(), mevcut bir nesnedeki bir metodun uygulamasını mutlaka değiştirmeden çağrılarını gözlemlemenizi sağlar. Gerekirse uygulamayı mock'layabilirsiniz de.

Örnek: Bir Modül Metodunu Gözetleme

// logger.js
export const logInfo = (message) => {
  console.log(`INFO: ${message}`);
};

// service.js
import { logInfo } from './logger';

export const performTask = (taskName) => {
  logInfo(`Görev başlatılıyor: ${taskName}`);
  // ... görev mantığı ...
  logInfo(`Görev ${taskName} tamamlandı.`);
};

// service.test.js
import { performTask } from './service';
import * as logger from './logger';

test('görev başlangıcını ve tamamlanmasını loglamalıdır', () => {
  const logSpy = jest.spyOn(logger, 'logInfo');

  performTask('backup');

  expect(logSpy).toHaveBeenCalledTimes(2);
  expect(logSpy).toHaveBeenCalledWith('Görev başlatılıyor: backup');
  expect(logSpy).toHaveBeenCalledWith('Görev backup tamamlandı.');

  logSpy.mockRestore(); // Orijinal uygulamayı geri yüklemek önemlidir
});

Modül Import'larını Mock'lama

Jest'in modül mock'lama yetenekleri geniştir. Tüm modülleri veya belirli export'ları mock'layabilirsiniz.

Örnek: Harici bir API İstemcisini Mock'lama

// api.js
import axios from 'axios';

export const getUser = async (userId) => {
  const response = await axios.get(`/api/users/${userId}`);
  return response.data;
};

// user-service.js
import { getUser } from './api';

export const getUserFullName = async (userId) => {
  const user = await getUser(userId);
  return `${user.firstName} ${user.lastName}`;
};

// user-service.test.js
import { getUserFullName } from './user-service';
import * as api from './api';

// Tüm api modülünü mock'la
jest.mock('./api');

test('mocklanmış API kullanarak tam adı almalıdır', async () => {
  // Mock'lanmış modülden belirli bir fonksiyonu mock'la
  api.getUser.mockResolvedValue({ id: 1, firstName: 'Ada', lastName: 'Lovelace' });

  const fullName = await getUserFullName(1);

  expect(fullName).toBe('Ada Lovelace');
  expect(api.getUser).toHaveBeenCalledTimes(1);
  expect(api.getUser).toHaveBeenCalledWith(1);
});

Otomatik Mocking ve Manuel Mocking

Jest, Node.js modüllerini otomatik olarak mock'lar. ES modülleri veya özel modüller için jest.mock()'a ihtiyacınız olabilir. Daha fazla kontrol için __mocks__ dizinleri oluşturabilirsiniz.

Mock Uygulamaları

Mock'larınız için özel uygulamalar sağlayabilirsiniz.

Örnek: Özel Bir Uygulama ile Mock'lama

// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

// calculator.js
import { add, subtract } from './math';

export const calculate = (operation, a, b) => {
  if (operation === 'add') {
    return add(a, b);
  } else if (operation === 'subtract') {
    return subtract(a, b);
  }
  return null;
};

// calculator.test.js
import { calculate } from './calculator';
import * as math from './math';

// Tüm math modülünü mock'la
jest.mock('./math');

test('mocklanmış math add kullanarak toplama yapmalıdır', () => {
  // 'add' fonksiyonu için bir mock uygulama sağla
  math.add.mockImplementation((a, b) => a + b + 10); // Sonuca 10 ekle
  math.subtract.mockReturnValue(5); // subtract fonksiyonunu da mock'la

  const result = calculate('add', 5, 3);

  expect(math.add).toHaveBeenCalledWith(5, 3);
  expect(result).toBe(18); // 5 + 3 + 10

  const subResult = calculate('subtract', 10, 2);
  expect(math.subtract).toHaveBeenCalledWith(10, 2);
  expect(subResult).toBe(5);
});

Snapshot Testleri: Kullanıcı Arayüzünü ve Yapılandırmayı Koruma

Snapshot testleri, bileşenlerinizin veya yapılandırmalarınızın çıktısını yakalamak için güçlü bir özelliktir. Özellikle UI testleri veya karmaşık veri yapılarının doğrulanması için kullanışlıdırlar.

Snapshot Testi Nasıl Çalışır?

Bir snapshot testi ilk kez çalıştığında, Jest test edilen değerin serileştirilmiş bir temsilini içeren bir .snap dosyası oluşturur. Sonraki çalıştırmalarda, Jest mevcut çıktıyı saklanan anlık görüntüyle karşılaştırır. Farklılarsa, test başarısız olur ve sizi istenmeyen değişiklikler konusunda uyarır. Bu, farklı bölgeler veya yerel ayarlardaki UI bileşenlerindeki regresyonları tespit etmek için paha biçilmezdir.

Örnek: Bir React Bileşeninin Snapshot'ını Alma

Bir React bileşeniniz olduğunu varsayalım:

// UserProfile.js
import React from 'react';

const UserProfile = ({ name, email, isActive }) => (
  <div>
    <h2>{name}</h2>
    <p><strong>E-posta:</strong> {email}</p>
    <p><strong>Durum:</strong> {isActive ? 'Aktif' : 'Pasif'}</p>
  </div>
);

export default UserProfile;

// UserProfile.test.js
import React from 'react';
import renderer from 'react-test-renderer'; // React bileşen snapshot'ları için
import UserProfile from './UserProfile';

test('UserProfile bileşenini doğru şekilde render eder', () => {
  const user = {
    name: 'Jane Doe',
    email: 'jane.doe@example.com',
    isActive: true,
  };
  const component = renderer.create(
    <UserProfile {...user} />
  );
  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

test('pasif UserProfile bileşenini doğru şekilde render eder', () => {
  const user = {
    name: 'John Smith',
    email: 'john.smith@example.com',
    isActive: false,
  };
  const component = renderer.create(
    <UserProfile {...user} />
  );
  const tree = component.toJSON();
  expect(tree).toMatchSnapshot('inactive user profile'); // İsimlendirilmiş snapshot
});

Testleri çalıştırdıktan sonra, Jest bir UserProfile.test.js.snap dosyası oluşturacaktır. Bileşeni güncellediğinizde, değişiklikleri gözden geçirmeniz ve potansiyel olarak Jest'i --updateSnapshot veya -u bayrağıyla çalıştırarak snapshot'ı güncellemeniz gerekecektir.

Snapshot Testi için En İyi Uygulamalar

Özel Eşleştiriciler: Test Okunabilirliğini Artırma

Jest'in yerleşik eşleştiricileri kapsamlıdır, ancak bazen kapsanmayan belirli koşulları iddia etmeniz gerekir. Özel eşleştiriciler, kendi iddia mantığınızı oluşturmanıza olanak tanıyarak testlerinizi daha anlamlı ve okunabilir hale getirir.

Özel Eşleştiriciler Oluşturma

Jest'in expect nesnesini kendi eşleştiricilerinizle genişletebilirsiniz.

Örnek: Geçerli Bir E-posta Formatını Kontrol Etme

Jest kurulum dosyanızda (örneğin, jest.config.js içinde yapılandırılmış jest.setup.js):

// jest.setup.js

expect.extend({
  toBeValidEmail(received) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    const pass = emailRegex.test(received);

    if (pass) {
      return {
        message: () => `beklenen ${received} değerinin geçerli bir e-posta olmamasıydı`,
        pass: true,
      };
    } else {
      return {
        message: () => `beklenen ${received} değerinin geçerli bir e-posta olmasıydı`,
        pass: false,
      };
    }
  },
});

// In your jest.config.js
// module.exports = { setupFilesAfterEnv: ['/jest.setup.js'] };

Test dosyanızda:

// validation.test.js

test('e-posta formatlarını doğrulamalıdır', () => {
  expect('test@example.com').toBeValidEmail();
  expect('invalid-email').not.toBeValidEmail();
  expect('another.test@sub.domain.co.uk').toBeValidEmail();
});

Özel Eşleştiricilerin Faydaları

Asenkron İşlemleri Test Etme

JavaScript büyük ölçüde asenkrondur. Jest, promise'leri ve async/await'i test etmek için mükemmel destek sağlar.

async/await Kullanımı

Bu, asenkron kodu test etmenin modern ve en okunabilir yoludur.

Örnek: Bir Asenkron Fonksiyonu Test Etme

// dataService.js
export const fetchUserData = async (userId) => {
  // Bir gecikmeden sonra veri alımını simüle et
  await new Promise(resolve => setTimeout(resolve, 50));
  if (userId === 1) {
    return { id: 1, name: 'Alice' };
  } else {
    throw new Error('Kullanıcı bulunamadı');
  }
};

// dataService.test.js
import { fetchUserData } from './dataService';

test('kullanıcı verilerini doğru şekilde getirir', async () => {
  const user = await fetchUserData(1);
  expect(user).toEqual({ id: 1, name: 'Alice' });
});

test('var olmayan kullanıcı için hata fırlatır', async () => {
  await expect(fetchUserData(2)).rejects.toThrow('Kullanıcı bulunamadı');
});

.resolves ve .rejects Kullanımı

Bu eşleştiriciler, promise çözümlemelerini ve reddetmelerini test etmeyi basitleştirir.

Örnek: .resolves/.rejects Kullanımı

// dataService.test.js (devamı)

test('.resolves ile kullanıcı verilerini getirir', () => {
  return expect(fetchUserData(1)).resolves.toEqual({ id: 1, name: 'Alice' });
});

test('.rejects ile var olmayan kullanıcı için hata fırlatır', () => {
  return expect(fetchUserData(2)).rejects.toThrow('Kullanıcı bulunamadı');
});

Zamanlayıcıları Yönetme

setTimeout veya setInterval kullanan fonksiyonlar için Jest, zamanlayıcı kontrolü sağlar.

Örnek: Zamanlayıcıları Kontrol Etme

// delayedGreeter.js
export const greetAfterDelay = (name, callback) => {
  setTimeout(() => {
    callback(`Merhaba, ${name}!`);
  }, 1000);
};

// delayedGreeter.test.js
import { greetAfterDelay } from './delayedGreeter';

jest.useFakeTimers(); // Sahte zamanlayıcıları etkinleştir

test('gecikmeden sonra selamlar', () => {
  const mockCallback = jest.fn();
  greetAfterDelay('Dünya', mockCallback);

  // Zamanlayıcıları 1000ms ilerlet
  jest.advanceTimersByTime(1000);

  expect(mockCallback).toHaveBeenCalledTimes(1);
  expect(mockCallback).toHaveBeenCalledWith('Merhaba, Dünya!');
});

// Başka bir yerde gerekirse gerçek zamanlayıcıları geri yükle
jest.useRealTimers();

Test Organizasyonu ve Yapısı

Test paketiniz büyüdükçe, sürdürülebilirlik için organizasyon kritik hale gelir.

Describe Blokları ve It Blokları

İlgili testleri gruplamak için describe ve bireysel test senaryoları için it (veya test) kullanın. Bu yapı, uygulamanın modülerliğini yansıtır.

Örnek: Yapılandırılmış Testler

describe('Kullanıcı Kimlik Doğrulama Servisi', () => {
  let authService;

  beforeEach(() => {
    // Her testten önce mock'ları veya hizmet örneklerini ayarla
    authService = require('./authService');
    jest.spyOn(authService, 'login').mockImplementation(() => Promise.resolve({ token: 'fake_token' }));
  });

  afterEach(() => {
    // Mock'ları temizle
    jest.restoreAllMocks();
  });

  describe('giriş yapma işlevselliği', () => {
    it('geçerli kimlik bilgileriyle bir kullanıcının başarılı bir şekilde giriş yapmasını sağlamalıdır', async () => {
      const result = await authService.login('user@example.com', 'password123');
      expect(result.token).toBeDefined();
      // ... daha fazla iddia ...
    });

    it('geçersiz kimlik bilgileriyle girişi başarısız kılmalıdır', async () => {
      jest.spyOn(authService, 'login').mockRejectedValue(new Error('Geçersiz kimlik bilgileri'));
      await expect(authService.login('user@example.com', 'wrong_password')).rejects.toThrow('Geçersiz kimlik bilgileri');
    });
  });

  describe('oturumu kapatma işlevselliği', () => {
    it('kullanıcı oturumunu temizlemelidir', async () => {
      // Oturumu kapatma mantığını test et...
    });
  });
});

Kurulum ve Kaldırma Hook'ları

Bu hook'lar, mock verileri, veritabanı bağlantılarını ayarlamak veya testler arasında kaynakları temizlemek için gereklidir.

Küresel Kitleler için Test Etme

Küresel bir kitle için uygulama geliştirirken, testle ilgili hususlar genişler:

Uluslararasılaştırma (i18n) ve Yerelleştirme (l10n)

Kullanıcı arayüzünüzün ve mesajlarınızın farklı dillere ve bölgesel formatlara doğru şekilde uyarlandığından emin olun.

Örnek: Yerelleştirilmiş tarih biçimlendirmesini test etme

// dateUtils.js
export const formatLocalizedDate = (date, locale) => {
  return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'numeric', day: 'numeric' }).format(date);
};

// dateUtils.test.js
import { formatLocalizedDate } from './dateUtils';

test('tarihi ABD yerel ayarı için doğru şekilde biçimlendirir', () => {
  const date = new Date(2023, 10, 15); // 15 Kasım 2023
  expect(formatLocalizedDate(date, 'en-US')).toBe('11/15/2023');
});

test('tarihi Alman yerel ayarı için doğru şekilde biçimlendirir', () => {
  const date = new Date(2023, 10, 15);
  expect(formatLocalizedDate(date, 'de-DE')).toBe('15.11.2023');
});

Zaman Dilimi Farkındalığı

Uygulamanızın farklı zaman dilimlerini, özellikle zamanlama veya gerçek zamanlı güncellemeler gibi özellikler için nasıl ele aldığını test edin. Sistem saatini mock'lamak veya zaman dilimlerini soyutlayan kütüphaneler kullanmak faydalı olabilir.

Verilerdeki Kültürel Nüanslar

Sayıların, para birimlerinin ve diğer veri temsillerinin kültürler arasında nasıl farklı algılanabileceğini veya beklenebileceğini düşünün. Özel eşleştiriciler burada özellikle yararlı olabilir.

İleri Teknikler ve Stratejiler

Test Odaklı Geliştirme (TDD) ve Davranış Odaklı Geliştirme (BDD)

Jest, TDD (Kırmızı-Yeşil-Yeniden Düzenle) ve BDD (Verilen-Ne Zaman-O Zaman) metodolojileriyle iyi uyum sağlar. Uygulama kodunu yazmadan önce istenen davranışı tanımlayan testler yazın. Bu, kodun en başından itibaren test edilebilirlik düşünülerek yazılmasını sağlar.

Jest ile Entegrasyon Testi

Jest birim testlerinde başarılı olsa da, entegrasyon testleri için de kullanılabilir. Daha az bağımlılığı mock'lamak veya Jest'in runInBand seçeneği gibi araçları kullanmak yardımcı olabilir.

Örnek: API Etkileşimini Test Etme (basitleştirilmiş)

// apiService.js
import axios from 'axios';

const API_BASE_URL = 'https://api.example.com';

export const createProduct = async (productData) => {
  const response = await axios.post(`${API_BASE_URL}/products`, productData);
  return response.data;
};

// apiService.test.js (Entegrasyon testi)
import axios from 'axios';
import { createProduct } from './apiService';

// Ağ katmanını kontrol etmek için entegrasyon testlerinde axios'u mock'la
jest.mock('axios');

test('API aracılığıyla bir ürün oluşturur', async () => {
  const mockProduct = { id: 1, name: 'Gadget' };
  const responseData = { success: true, product: mockProduct };

  axios.post.mockResolvedValue({
    data: responseData,
    status: 201,
    headers: { 'content-type': 'application/json' },
  });

  const newProductData = { name: 'Gadget', price: 99.99 };
  const result = await createProduct(newProductData);

  expect(axios.post).toHaveBeenCalledWith(`${process.env.API_BASE_URL || 'https://api.example.com'}/products`, newProductData);
  expect(result).toEqual(responseData);
});

Paralellik ve Yapılandırma

Jest, yürütmeyi hızlandırmak için testleri paralel olarak çalıştırabilir. Bunu jest.config.js dosyanızda yapılandırın. Örneğin, maxWorkers ayarı paralel işlem sayısını kontrol eder.

Kapsam Raporları

Kod tabanınızın test edilmeyen kısımlarını belirlemek için Jest'in yerleşik kapsam raporlamasını kullanın. Ayrıntılı raporlar oluşturmak için testleri --coverage ile çalıştırın.

jest --coverage

Kapsam raporlarını gözden geçirmek, gelişmiş test kalıplarınızın uluslararasılaştırma ve yerelleştirme kod yolları da dahil olmak üzere kritik mantığı etkili bir şekilde kapsadığından emin olmanıza yardımcı olur.

Sonuç

Gelişmiş Jest test kalıplarında ustalaşmak, küresel bir kitle için güvenilir, sürdürülebilir ve yüksek kaliteli yazılımlar oluşturmaya yönelik önemli bir adımdır. Mocking, snapshot testi, özel eşleştiriciler ve asenkron test tekniklerini etkili bir şekilde kullanarak, test paketinizin sağlamlığını artırabilir ve uygulamanızın çeşitli senaryolar ve bölgelerdeki davranışına daha fazla güven duyabilirsiniz. Bu kalıpları benimsemek, dünya çapındaki geliştirme ekiplerine olağanüstü kullanıcı deneyimleri sunma gücü verir.

JavaScript test uygulamalarınızı bir üst seviyeye taşımak için bu gelişmiş teknikleri bugünden iş akışınıza dahil etmeye başlayın.

Jest: Sağlam Yazılımlar için Gelişmiş Test Kalıpları | MLOG