Software Testing Best Practices ve Stratejileri
Yazılım testleri, kod kalitesi ve güvenilirliğin temelidir.
Test Piramidi
/\
/E2E\ ← Az (Yavaş, Pahalı)
/------\
/Integration\ ← Orta
/-----------\
/ Unit \ ← Çok (Hızlı, Ucuz)
/-------------\
Unit Testing
Jest Example
// math.js
export function add(a, b) {
return a + b;
}
export function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
// math.test.js
import { add, divide } from './math';
describe('Math functions', () => {
test('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
test('divides two numbers', () => {
expect(divide(10, 2)).toBe(5);
});
test('throws error on division by zero', () => {
expect(() => divide(10, 0)).toThrow('Division by zero');
});
});
React Component Testing
// Button.jsx
export function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
// Button.test.jsx
import { render, fireEvent, screen } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
test('renders button text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click</Button>);
fireEvent.click(screen.getByText('Click'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Integration Testing
// api.test.js
import request from 'supertest';
import app from './app';
import { setupTestDb, teardownTestDb } from './test-utils';
beforeAll(async () => {
await setupTestDb();
});
afterAll(async () => {
await teardownTestDb();
});
describe('User API', () => {
test('POST /users creates a new user', async () => {
const response = await request(app)
.post('/api/users')
.send({
name: 'John Doe',
email: 'john@example.com'
})
.expect(201);
expect(response.body).toHaveProperty('id');
expect(response.body.name).toBe('John Doe');
});
test('GET /users returns all users', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);
expect(Array.isArray(response.body)).toBe(true);
});
});
E2E Testing
Playwright Example
// e2e/login.spec.js
import { test, expect } from '@playwright/test';
test.describe('Login Flow', () => {
test('successful login', async ({ page }) => {
await page.goto('http://localhost:3000/login');
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('http://localhost:3000/dashboard');
await expect(page.locator('.user-name')).toContainText('John Doe');
});
test('login with invalid credentials', async ({ page }) => {
await page.goto('http://localhost:3000/login');
await page.fill('input[name="email"]', 'wrong@example.com');
await page.fill('input[name="password"]', 'wrongpass');
await page.click('button[type="submit"]');
await expect(page.locator('.error')).toContainText('Invalid credentials');
});
});
Mocking
// userService.js
export async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// userService.test.js
import { fetchUser } from './userService';
global.fetch = jest.fn();
test('fetches user successfully', async () => {
const mockUser = { id: 1, name: 'John' };
fetch.mockResolvedValueOnce({
json: async () => mockUser
});
const user = await fetchUser(1);
expect(fetch).toHaveBeenCalledWith('/api/users/1');
expect(user).toEqual(mockUser);
});
Test Coverage
{
"scripts": {
"test": "jest",
"test:coverage": "jest --coverage"
},
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
TDD (Test-Driven Development)
// 1. Red - Test yaz (fail)
test('validates email format', () => {
expect(validateEmail('invalid')).toBe(false);
expect(validateEmail('test@example.com')).toBe(true);
});
// 2. Green - Minimal kod yaz (pass)
function validateEmail(email) {
return email.includes('@');
}
// 3. Refactor - İyileştir
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
Snapshot Testing
import renderer from 'react-test-renderer';
import { Card } from './Card';
test('Card component renders correctly', () => {
const tree = renderer
.create(<Card title="Test" description="Description" />)
.toJSON();
expect(tree).toMatchSnapshot();
});
Testlerle güvenilir yazılımlar geliştirin!