← Назад к вопросам

Какие знаешь виды тестов?

1.0 Junior🔥 251 комментариев
#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Виды тестов в backend разработке

Это не просто знание терминов, но понимание, когда использовать каждый. Расскажу о пирамиде тестов:

Пирамида тестов (по объему)

       E2E Tests (10%)
      /            \
     /              \
  Integration       Tests (30%)
  /                        \
 /                          \
Unit Tests (60%)

1. Unit тесты (60% от всех)

Определение: Тестируешь отдельную функцию/метод изолированно

// Функция для тестирования
function calculateDiscount(price, discountPercent) {
  if (discountPercent < 0 || discountPercent > 100) {
    throw new Error('Invalid discount');
  }
  return price * (1 - discountPercent / 100);
}

// Unit тест
describe('calculateDiscount', () => {
  it('should apply discount correctly', () => {
    expect(calculateDiscount(100, 20)).toBe(80);
  });
  
  it('should throw on invalid discount', () => {
    expect(() => calculateDiscount(100, 150)).toThrow();
  });
  
  it('should handle 0 discount', () => {
    expect(calculateDiscount(100, 0)).toBe(100);
  });
});

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

  • Быстрые (< 100ms каждый)
  • Не нужна БД, сеть
  • Мокируешь dependencies
  • Легко писать и поддерживать

Best practices:

// ✅ ХОРОШО: Arrange-Act-Assert pattern
describe('User service', () => {
  it('should create user with valid email', () => {
    // Arrange
    const userService = new UserService();
    const userData = { email: 'test@example.com', name: 'John' };
    
    // Act
    const result = userService.validate(userData);
    
    // Assert
    expect(result.isValid).toBe(true);
  });
});

// ❌ ПЛОХО: Слишком общее утверждение
it('should work', () => {
  expect(calculateDiscount(100, 20)).toBeTruthy();
});

2. Integration тесты (30% от всех)

Определение: Тестируешь взаимодействие между компонентами (например, controller + service + DB)

// Тестирование API endpoint с БД
describe('POST /api/users', () => {
  let db;
  
  beforeAll(async () => {
    db = new Database();
    await db.connect();
  });
  
  afterAll(async () => {
    await db.disconnect();
  });
  
  afterEach(async () => {
    await db.clearTable('users');
  });
  
  it('should create user in database', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'john@example.com', name: 'John' });
    
    expect(response.status).toBe(201);
    expect(response.body.id).toBeDefined();
    
    // Проверяем, что данные в БД
    const user = await db.users.findOne({ email: 'john@example.com' });
    expect(user).toBeDefined();
    expect(user.name).toBe('John');
  });
  
  it('should reject duplicate email', async () => {
    await request(app)
      .post('/api/users')
      .send({ email: 'john@example.com', name: 'John' });
    
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'john@example.com', name: 'Jane' });
    
    expect(response.status).toBe(409); // Conflict
  });
});

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

  • Медленнее unit тестов (1-5 сек)
  • Используешь реальную/тестовую БД
  • Можешь мокировать external APIs
  • Проверяешь happy path и error cases

Когда нужны:

  • API endpoints
  • Database interactions
  • Service layer logic

3. E2E тесты (10% от всех)

Определение: Тестируешь entire user flow от старта до конца

// Пример с Playwright
describe('User registration flow', () => {
  it('should complete user registration', async () => {
    const { page } = await initBrowser();
    
    // Navigate to registration
    await page.goto('http://localhost:3000/register');
    
    // Fill form
    await page.fill('input[name="email"]', 'newuser@example.com');
    await page.fill('input[name="password"]', 'SecurePassword123');
    await page.fill('input[name="confirmPassword"]', 'SecurePassword123');
    
    // Submit
    await page.click('button[type="submit"]');
    
    // Check redirect to dashboard
    await page.waitForNavigation();
    expect(page.url()).toContain('/dashboard');
    
    // Verify user is logged in
    const userName = await page.textContent('[data-testid="user-name"]');
    expect(userName).toBe('newuser@example.com');
  });
});

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

  • Очень медленные (10-60 сек)
  • Тестируют реальный browser
  • Проверяют UI, interactions
  • Только critical paths

4. Contract тесты (emerging)

Определение: Тестируешь контракт между сервисами (например, API response format)

// Микросервис A: Users Service
describe('GET /api/users/:id contract', () => {
  it('should return user with required fields', async () => {
    const response = await request(app)
      .get('/api/users/1');
    
    expect(response.body).toMatchObject({
      id: expect.any(Number),
      email: expect.any(String),
      name: expect.any(String),
      createdAt: expect.any(String), // ISO format
    });
  });
});

// Микросервис B: Orders Service (консумирует Users API)
describe('Users Service contract', () => {
  it('should receive user data in expected format', async () => {
    // Используешь mock из Users Service
    const userProvider = require('pact').Pact(...);
    
    userProvider.addInteraction({
      state: 'user with id 1 exists',
      uponReceiving: 'a request for user 1',
      withRequest: { method: 'GET', path: '/api/users/1' },
      willRespondWith: {
        status: 200,
        body: {
          id: 1,
          email: 'john@example.com',
          name: 'John Doe',
          createdAt: '2024-01-01T00:00:00Z'
        }
      }
    });
  });
});

5. Load тесты (performance)

Определение: Тестируешь приложение под нагрузкой

// k6 script
import http from 'k6/http';
import { check } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 100 },  // Ramp up
    { duration: '5m', target: 100 },  // Stay
    { duration: '2m', target: 0 },    // Ramp down
  ],
};

export default function() {
  const response = http.get('http://localhost:3000/api/users/1');
  
  check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
}

6. Security тесты

Определение: Проверяешь защиту от common vulnerabilities

describe('Security tests', () => {
  it('should prevent SQL injection', async () => {
    const response = await request(app)
      .get('/api/users/1 OR 1=1');
    
    expect(response.status).toBe(400);
  });
  
  it('should validate JWT token', async () => {
    const response = await request(app)
      .get('/api/protected')
      .set('Authorization', 'Bearer invalid_token');
    
    expect(response.status).toBe(401);
  });
  
  it('should not expose sensitive headers', async () => {
    const response = await request(app).get('/');
    expect(response.headers['x-powered-by']).toBeUndefined();
  });
});

7. Snapshot тесты (осторожно!)

Определение: Сохраняешь output и сравниваешь с будущими версиями

// Можно, но опасно
it('should return expected user object', () => {
  const user = new User({ id: 1, name: 'John' });
  expect(user).toMatchSnapshot();
});

// Более безопасно: проверяй specific fields
it('should return user with correct fields', () => {
  const user = new User({ id: 1, name: 'John' });
  expect(user).toEqual({
    id: 1,
    name: 'John',
    createdAt: expect.any(Date)
  });
});

8. Mutation тесты (продвинутый уровень)

Определение: Инструмент ломает код и проверяет, упадут ли тесты

npm install stryker
stryker run

Если Stryker может удалить строку кода и тесты всё равно проходят → твой тест слабый.

Рекомендуемое распределение

Всего тестов:
  - 60% Unit tests (быстрые, many)
  - 30% Integration tests (medium speed, few)
  - 10% E2E tests (медленные, only critical flows)
  - Contract tests (between microservices)
  - Load tests (перед production release)

Tools, которые я использую

Unit/Integration:

  • jest (самый популярный)
  • mocha + chai
  • vitest (faster than jest)

E2E:

  • Playwright (самый modern)
  • Cypress (frontend-heavy)
  • Selenium (legacy, но всё ещё work)

Load:

  • k6 (самый easy to use)
  • wrk (для http нагрузки)
  • Apache JMeter (oldschool, но powerful)

Monitoring:

  • Istanbul/nyc (coverage reports)
  • Sonarqube (code quality)

Итог

Жизнь без тестов = playing with fire в production. Каждый тип тестов решает свою проблему:

  • Unit тесты = быстрая feedback при разработке
  • Integration тесты = реальные сценарии работают
  • E2E тесты = user не сломает приложение
  • Load тесты = масштабируемость гарантирована