Jest, JavaScript ve TypeScript ekosisteminin en popüler test framework''üdür. Zero-config başlar, built-in assertion, mocking ve coverage ile gelir. Bu yazı Jest''in tüm temel özelliklerini ve modern test pratiklerini anlatır.
Kurulum
İlgili rehberler: Yazılım geliştirme süreçleri · PostgreSQL optimizasyonu · Git ileri seviye komutlar · Redis nedir, nasıl kullanılır · Docker ile deploy
npm i -D jest @types/jest
npm i -D ts-jest typescript # TS projesi için
# package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
# TypeScript için
npx ts-jest config:init
İlk Test
// math.js
function add(a, b) { return a + b; }
module.exports = { add };
// math.test.js
const { add } = require('./math');
describe('add()', () => {
it('iki pozitif sayıyı toplar', () => {
expect(add(2, 3)).toBe(5);
});
it('negatif sayıları toplar', () => {
expect(add(-1, -2)).toBe(-3);
});
it('sıfır ile çalışır', () => {
expect(add(0, 5)).toBe(5);
});
});
npm test
# PASS ./math.test.js
# add()
# ✓ iki pozitif sayıyı toplar (2 ms)
# ✓ negatif sayıları toplar
# ✓ sıfır ile çalışır
Matcher''lar
// Eşitlik
expect(x).toBe(5); // primitive ===
expect(obj).toEqual({ a: 1 }); // deep eşitlik
expect(obj).toStrictEqual({ a: 1 }); // + tip kontrolü
// Truthy/Falsy
expect(x).toBeTruthy();
expect(x).toBeNull();
expect(x).toBeDefined();
// Sayı
expect(x).toBeGreaterThan(3);
expect(x).toBeCloseTo(0.3); // float
// String / Array
expect(s).toMatch(/error/i);
expect(arr).toContain('apple');
expect(arr).toHaveLength(3);
// Object
expect(user).toHaveProperty('email');
expect(user).toMatchObject({ name: 'Ali' }); // subset
// Exception
expect(() => throwError()).toThrow('Bad input');
// Negate
expect(x).not.toBe(0);
Setup ve Teardown
describe('User service', () => {
let db;
beforeAll(async () => {
db = await connectDB();
});
afterAll(async () => {
await db.close();
});
beforeEach(async () => {
await db.clear();
});
it('creates user', async () => {
const user = await createUser(db, { email: 'a@b.com' });
expect(user.id).toBeDefined();
});
});
Async Test
// Promise ile
it('fetches user', () => {
return getUser(1).then(user => {
expect(user.name).toBe('Ali');
});
});
// async/await (tercih edilen)
it('fetches user', async () => {
const user = await getUser(1);
expect(user.name).toBe('Ali');
});
// Reject bekle
it('throws on invalid id', async () => {
await expect(getUser(-1)).rejects.toThrow('Invalid');
});
// Resolve bekle
it('resolves with data', async () => {
await expect(getUser(1)).resolves.toEqual(expect.objectContaining({ name: 'Ali' }));
});
Mock Functions
// Jest mock
const logger = jest.fn();
logger('hello');
logger('world');
expect(logger).toHaveBeenCalled();
expect(logger).toHaveBeenCalledTimes(2);
expect(logger).toHaveBeenCalledWith('hello');
expect(logger).toHaveBeenNthCalledWith(1, 'hello');
// Mock return value
const fetchUser = jest.fn().mockResolvedValue({ id: 1, name: 'Ali' });
const errFetch = jest.fn().mockRejectedValue(new Error('Network'));
// Implementation
const add = jest.fn((a, b) => a + b);
expect(add(2, 3)).toBe(5);
Module Mock
// __mocks__/axios.js
module.exports = {
get: jest.fn(),
post: jest.fn()
};
// user.test.js
jest.mock('axios');
const axios = require('axios');
it('fetches user', async () => {
axios.get.mockResolvedValue({ data: { name: 'Ali' } });
const user = await getUser(1);
expect(user).toEqual({ name: 'Ali' });
expect(axios.get).toHaveBeenCalledWith('/users/1');
});
Spy
// Var olan fonksiyonu izle, çağırmaya devam et
const spy = jest.spyOn(console, 'log');
myFunction();
expect(spy).toHaveBeenCalledWith('doing work');
spy.mockRestore(); // orijinale dön
Snapshot Testing
it('renders correctly', () => {
const { container } = render(<Button label="Save" />);
expect(container).toMatchSnapshot();
});
// İlk çalıştırmada snapshot oluşturur
// Sonraki çalıştırmada karşılaştırır — fark varsa fail
// Kabul etmek için: jest -u (update)
Coverage
npm test -- --coverage
# jest.config.js
module.exports = {
coverageDirectory: 'coverage',
collectCoverageFrom: ['src/**/*.{js,ts}', '!**/*.test.{js,ts}'],
coverageThreshold: {
global: { branches: 80, functions: 80, lines: 80, statements: 80 }
}
};
Test Driven Development (TDD)
- Red: Failing test yaz (fonksiyon daha yok)
- Green: En basit kodu yaz, test geçsin
- Refactor: Kodu temizle, test hâlâ geçer
- Tekrar
Vitest Alternatifi
Vite projesi kullanıyorsanız Jest yerine Vitest''i düşünün. API %95 Jest uyumlu, 3-5x hızlı, ESM native, TS first-class.
Yaygın Hatalar
- Async test''te
awaitunutmak → false positive - Her testte DB clear etmemek → flaky tests
- Snapshot''ları büyük tutmak → review imkansız
- Implementation detail test etmek → refactor''da kırılır
- Test izolasyonu yok → test sırası sonucu etkiler
Modern Yazılım Geliştirme ve DevOps Pratikleri
Profesyonel yazılım geliştirme süreci üç pillar üzerine kuruludur: kaynak kontrolü (Git + GitHub/GitLab pull request akışı, code review zorunlu), CI/CD pipeline (otomatik test + lint + build + deploy), ve gözlemlenebilirlik (Sentry/Datadog/Grafana ile log, metric, trace toplama). Test piramidi (unit > integration > e2e) ile kod kalitesini garantilemek, mikroservis mimarisinde Docker container ve Kubernetes orkestrasyonu kullanmak, REST veya GraphQL API tasarımında OpenAPI/GraphQL Schema sözleşmesi tutmak modern standardlardır. Yazılım geliştirme yaşam döngüsü boyunca (gereksinim → tasarım → implementasyon → test → deploy → bakım) Agile/Scrum sprintleri 1-2 hafta, DevOps takımları sürekli teslim (continuous delivery) prensibiyle çalışır.
Jest, Vitest veya Playwright ile test piramidi ve CI entegrasyonu için iletişime geçin