Перейти к содержимому

2. Типы тестов

Testing Types

Тестируют одну единицу кода (функцию, класс, компонент) в изоляции.

// Функция
function formatPrice(amount, currency = 'RUB') {
return `${amount.toFixed(2)} ${currency}`;
}
// Unit тест
test('formatPrice formats correctly', () => {
expect(formatPrice(1500)).toBe('1500.00 RUB');
expect(formatPrice(99.9, 'USD')).toBe('99.90 USD');
});
// Тест класса
class BankAccount {
constructor(balance = 0) { this.balance = balance; }
deposit(amount) {
if (amount <= 0) throw new Error('Invalid amount');
this.balance += amount;
}
withdraw(amount) {
if (amount > this.balance) throw new Error('Insufficient funds');
this.balance -= amount;
}
}
describe('BankAccount', () => {
let account;
beforeEach(() => { account = new BankAccount(100); });
test('deposit increases balance', () => {
account.deposit(50);
expect(account.balance).toBe(150);
});
test('withdraw decreases balance', () => {
account.withdraw(30);
expect(account.balance).toBe(70);
});
test('withdraw throws if insufficient funds', () => {
expect(() => account.withdraw(200)).toThrow('Insufficient funds');
});
test('deposit throws for invalid amount', () => {
expect(() => account.deposit(-10)).toThrow('Invalid amount');
});
});

Характеристики:

  • Выполняются за миллисекунды
  • Не зависят от сети, БД, файлов
  • Зависимости заменяются моками

Тестируют взаимодействие нескольких частей системы.

// Тест API endpoint (сервер + БД)
import request from 'supertest';
import app from '../app';
import { db } from '../db';
describe('POST /api/users', () => {
beforeEach(async () => {
await db.query('TRUNCATE users CASCADE');
});
afterAll(async () => {
await db.end();
});
test('creates user successfully', async () => {
const res = await request(app)
.post('/api/users')
.send({ name: 'Alice', email: '[email protected]' })
.expect(201);
expect(res.body).toMatchObject({
id: expect.any(Number),
name: 'Alice',
});
// Проверяем реально записано в БД
const { rows } = await db.query('SELECT * FROM users WHERE email = $1', ['[email protected]']);
expect(rows).toHaveLength(1);
});
test('returns 400 for duplicate email', async () => {
await request(app).post('/api/users').send({ name: 'Alice', email: '[email protected]' });
const res = await request(app)
.post('/api/users')
.send({ name: 'Bob', email: '[email protected]' })
.expect(400);
expect(res.body.error).toMatch(/already exists/i);
});
});

Тестируют полный сценарий как настоящий пользователь в браузере.

// Playwright
import { test, expect } from '@playwright/test';
test('user can login and see dashboard', async ({ page }) => {
// Открыть страницу
await page.goto('http://localhost:3000');
// Заполнить форму входа
await page.fill('[data-testid="email"]', '[email protected]');
await page.fill('[data-testid="password"]', 'password123');
await page.click('[data-testid="login-button"]');
// Убедиться, что попали на дашборд
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Добро пожаловать, Alice');
// Проверить наличие элементов
await expect(page.locator('[data-testid="stats-card"]')).toBeVisible();
});
UnitIntegrationE2E
Скорость< 1msСотни msСекунды
Стоимость написанияДёшевоСреднеДорого
ПоддержкаЛегкоСреднеСложно
УверенностьНизкаяСредняяВысокая
ОхватОдна функцияНесколько модулейВесь сценарий
  1. Напиши unit тест для функции валидации email
  2. Напиши integration тест для API ендпойнта с mock базой данных
  3. Объясни: почему E2E тесты более “хрупкие”?
  • Unit: изолированно, быстро, много
  • Integration: реальное взаимодействие модулей
  • E2E: полный пользовательский сценарий
  • Сбалансированная пирамида = надёжность + скорость