Тестирование программного обеспечения организовано в отдельные уровни, каждый из которых нацелен на различные аспекты системы и вовлекает разных членов команды. Понимание уровней тестирования помогает командам структурировать свою стратегию тестирования, распределять ответственность и обеспечивать комплексное покрытие качества от индивидуальных компонентов до полных систем.

Что Такое Уровни Тестирования?

Уровни тестирования представляют этапы в жизненном цикле разработки ПО, где происходят активности тестирования. Каждый уровень имеет специфические цели, базу тестирования, объекты тестирования и типичные дефекты для обнаружения.

Четыре Основных Уровня Тестирования

┌─────────────────────────────────────────────┐
│    User Acceptance Testing (UAT)            │ ← Бизнес валидирует требования
├─────────────────────────────────────────────┤
│            System Testing                   │ ← Поведение полной системы
├─────────────────────────────────────────────┤
│         Integration Testing                 │ ← Взаимодействия компонентов
├─────────────────────────────────────────────┤
│            Unit Testing                     │ ← Индивидуальные компоненты
└─────────────────────────────────────────────┘

Каждый уровень строится на предыдущем, при этом дефекты в идеале обнаруживаются на самой ранней возможной стадии для минимизации стоимости и сложности. Для глубокого понимания различных подходов к тестированию через эти уровни, смотрите наше сравнение подходов к тестированию.

Unit Testing

Unit testing проверяет индивидуальные компоненты (функции, методы, классы) изолированно. Это основа пирамиды тестирования.

Цели

  • Проверить, что каждая единица работает согласно дизайну
  • Обнаружить логические ошибки рано в разработке
  • Обеспечить безопасный рефакторинг через обнаружение регрессий
  • Документировать ожидаемое поведение единиц кода
  • Предоставлять быструю обратную связь разработчикам

База Тестирования

  • Документы детального дизайна
  • Реализация кода
  • Спецификации компонентов
  • Документация API

Кто Выполняет Unit Testing?

Преимущественно разработчики, часто используя Test-Driven Development (TDD):

# Пример: Unit test для сервиса аутентификации
import pytest
from auth_service import AuthService, InvalidCredentialsError

class TestAuthService:
    def setup_method(self):
        """Настроить фикстуры перед каждым тестом"""
        self.auth = AuthService(database="test_db")
 (как обсуждается в [Entry and Exit Criteria in Software Testing: When to Start and Stop Testing](/blog/entry-exit-criteria))        self.valid_user = {
            "email": "test@example.com",
            "password": "SecurePass123!"
        }

    def test_successful_authentication(self):
        """Тест что валидные учётные данные возвращают auth token"""
        token = self.auth.authenticate(
            self.valid_user["email"],
            self.valid_user["password"]
        )

        assert token is not None
        assert len(token) == 64  # Длина JWT token
        assert self.auth.is_token_valid(token) is True

    def test_invalid_password_raises_error(self):
        """Тест что невалидный пароль генерирует соответствующую ошибку"""
        with pytest.raises(InvalidCredentialsError) as exc_info:
            self.auth.authenticate(
                self.valid_user["email"],
                "WrongPassword"
            )

        assert "Invalid credentials" in str(exc_info.value)

    def test_nonexistent_user_raises_error(self):
        """Тест что несуществующий пользователь генерирует ошибку"""
        with pytest.raises(InvalidCredentialsError):
            self.auth.authenticate(
                "nonexistent@example.com",
                "anypassword"
            )

    def test_account_lockout_after_failed_attempts(self):
        """Тест что аккаунт блокируется после 5 неудачных попыток входа"""
        # Попытка 5 неудачных входов
        for _ in range(5):
            with pytest.raises(InvalidCredentialsError):
                self.auth.authenticate(
                    self.valid_user["email"],
                    "WrongPassword"
                )

        # 6-я попытка должна вызвать AccountLockedError
        from auth_service import AccountLockedError
        with pytest.raises(AccountLockedError):
            self.auth.authenticate(
                self.valid_user["email"],
                self.valid_user["password"]  # Даже с правильным паролем
            )

    def test_token_expiration(self):
        """Тест что токены истекают после настроенного времени"""
        import time

        token = self.auth.authenticate(
            self.valid_user["email"],
            self.valid_user["password"]
        )

        # Имитировать прохождение времени (в реальном коде использовать библиотеку time mocking)
        self.auth._set_time_offset(3600)  # +1 час

        assert self.auth.is_token_valid(token) is False

Типичные Найденные Дефекты

  • Неправильные вычисления или логика
  • Ошибки граничных значений
  • Исключения нулевого указателя
  • Неправильные типы переменных
  • Ошибки циклов (off-by-one, бесконечные циклы)
  • Неправильная обработка ошибок

Лучшие Практики

  1. Следовать паттерну AAA: Arrange, Act, Assert
  2. Тестировать одну вещь за тест: Каждый тест должен проверять одно поведение
  3. Использовать описательные имена тестов: test_account_locks_after_5_failed_attempts
  4. Изолировать зависимости: Использовать mocks/stubs для внешних зависимостей
  5. Стремиться к высокому покрытию: Минимум 70-80%, критический код 100%
  6. Быстрое выполнение: Unit tests должны выполняться за миллисекунды

Integration Testing

Integration (как обсуждается в Dynamic Testing: Testing in Action) testing проверяет взаимодействия между интегрированными компонентами или системами. Он обнаруживает дефекты интерфейса, которые пропускают unit tests.

Цели

  • Проверить интерфейсы между компонентами
  • Протестировать поток данных между модулями
  • Валидировать контракты API
  • Обнаружить баги интеграции рано
  • Тестировать сценарии взаимодействия компонентов

База Тестирования

  • Документы дизайна программного обеспечения и системы
  • Диаграммы архитектуры
  • Спецификации API
  • Определения интерфейсов
  • Описания use case

Подходы к Интеграции

Интеграция Big Bang:

Все компоненты интегрированы одновременно → Тестировать всё сразу

Плюсы: Быстро настроить
Минусы: Трудно изолировать дефекты, рискованно

Инкрементальная Интеграция:

Компоненты интегрированы и протестированы инкрементально

Top-Down: Начать с высокоуровневых модулей, stub нижние уровни
Bottom-Up: Начать с низкоуровневых модулей, создать драйверы для высоких уровней
Sandwich: Комбинация top-down и bottom-up

Пример: API Integration Testing

// Пример: Тестирование интеграции между аутентификацией и сервисом пользователя
const request = require('supertest');
const app = require('../app');

describe('Интеграция Аутентификации и Сервиса Пользователя', () => {
  let authToken;
  let userId;

  beforeAll(async () => {
    // Настроить тестовую базу данных
    await setupTestDatabase() (как обсуждается в [Grey Box Testing: Best of Both Worlds](/blog/grey-box-testing));
  });

  afterAll(async () => {
    // Очистить тестовую базу данных
    await cleanupTestDatabase();
  });

  test('Регистрация пользователя создаёт пользователя и возвращает auth token', async () => {
    const newUser = {
      email: 'integration@test.com',
      password: 'SecurePass123!',
      name: 'Integration Test User'
    };

    const response = await request(app)
      .post('/api/auth/register')
      .send(newUser)
      .expect(201);

    // Проверить структуру ответа
    expect(response.body).toHaveProperty('token');
    expect(response.body).toHaveProperty('user');
    expect(response.body.user.email).toBe(newUser.email);

    // Сохранить для последующих тестов
    authToken = response.body.token;
    userId = response.body.user.id;
  });

  test('Аутентифицированный пользователь может получить доступ к эндпоинту профиля', async () => {
    const response = await request(app)
      .get(`/api/users/${userId}/profile`)
      .set('Authorization', `Bearer ${authToken}`)
      .expect(200);

    expect(response.body.email).toBe('integration@test.com');
    expect(response.body.name).toBe('Integration Test User');
  });

  test('Неаутентифицированный запрос к профилю возвращает 401', async () => {
    await request(app)
      .get(`/api/users/${userId}/profile`)
      .expect(401);
  });

  test('Невалидный токен возвращает 403', async () => {
    await request(app)
      .get(`/api/users/${userId}/profile`)
      .set('Authorization', 'Bearer invalid_token_here')
      .expect(403);
  });

  test('Пользователь может обновить профиль с валидным auth token', async () => {
    const updates = {
      name: 'Updated Name',
      bio: 'Integration testing is awesome'
    };

    const response = await request(app)
      .patch(`/api/users/${userId}/profile`)
      .set('Authorization', `Bearer ${authToken}`)
      .send(updates)
      .expect(200);

    expect(response.body.name).toBe('Updated Name');
    expect(response.body.bio).toBe('Integration testing is awesome');
  });

  test('Logout инвалидирует auth token', async () => {
    // Logout
    await request(app)
      .post('/api/auth/logout')
      .set('Authorization', `Bearer ${authToken}`)
      .expect(200);

    // Попытка доступа к профилю с инвалидированным токеном
    await request(app)
      .get(`/api/users/${userId}/profile`)
      .set('Authorization', `Bearer ${authToken}`)
      .expect(401);
  });
});

Типичные Найденные Дефекты

  • Неправильные параметры вызовов API
  • Несоответствия формата данных между компонентами
  • Отсутствующая или неправильная обработка ошибок между сервисами
  • Проблемы таймингов (условия гонки, дедлоки)
  • Неправильные предположения о поведении компонентов
  • Проблемы подключения к базе данных
  • Сбои коммуникации очереди сообщений

Лучшие Практики

  1. Тестировать реалистичные сценарии: Использовать реальные потоки интеграции
  2. Использовать тестовые базы данных: Изолировать от продакшн данных
  3. Тестировать условия ошибок: Сбои сети, таймауты, невалидные ответы
  4. Поддерживать тестовые данные: Создавать переиспользуемые фикстуры
  5. Запускать в CI/CD: Автоматизировать выполнение на каждой сборке
  6. Contract testing: Проверять API контракты между сервисами

System Testing

System testing валидирует полную интегрированную систему против заданных требований. Он тестирует end-to-end сценарии с перспективы пользователя.

Цели

  • Проверить, что система соответствует функциональным требованиям
  • Валидировать поведение системы в реалистичных окружениях
  • Тестировать нефункциональные требования (производительность, безопасность, юзабилити)
  • Проверить правильную интеграцию системы с внешними системами
  • Валидировать полные пользовательские рабочие потоки

База Тестирования

  • Спецификации требований системы и ПО
  • Use cases и user stories
  • Документы архитектуры системы
  • Описания бизнес-процессов
  • Пользовательская документация

Кто Выполняет System Testing?

Независимая тестовая команда или QA-инженеры, отдельные от команды разработки для обеспечения объективности.

Типы System Testing

ТипФокусПример
ФункциональныйФункции работают согласно спецификацииПроцесс оформления заказа e-commerce
ПроизводительностьВремя ответа, пропускная способностьЗагрузка 1000 одновременных пользователей
БезопасностьУязвимости, контроль доступаSQL injection, XSS атаки
ЮзабилитиПользовательский опыт, лёгкость использованияНавигация, валидация форм
СовместимостьРаботает в разных окруженияхКросс-браузерность, мобильные устройства
ВосстановлениеСистема восстанавливается после сбоевВосстановление после краха БД
УстановкаРазвёртывание и настройкаЧистая установка, сценарии обновления

Пример: Сценарии System Test

# Функциональный System Test: Поток Покупки E-commerce

Feature: Полный рабочий процесс покупки
  Как клиент
  Я хочу покупать продукты
  Чтобы получить их по своему адресу

Background:
  Given система e-commerce работает
  And тестовые продукты существуют в инвентаре
  And тестовая учётная запись пользователя "systemtest@example.com" существует

Scenario: Успешная покупка продукта с кредитной картой
  Given я аутентифицирован как "systemtest@example.com"
  When я ищу "wireless headphones"
  And выбираю "Sony WH-1000XM4" из результатов
  And нажимаю "Добавить в Корзину"
  And перехожу к оформлению заказа
  And ввожу адрес доставки:
    | Поле       | Значение           |
    | Улица      | 123 Test St        |
    | Город      | San Francisco      |
    | Штат       | CA                 |
    | Индекс     | 94105              |
  And выбираю "Кредитная Карта" как способ оплаты
  And ввожу валидные данные кредитной карты
  And подтверждаю заказ
  Then я должен увидеть сообщение "Заказ Подтверждён"
  And я должен получить email подтверждения заказа в течение 2 минут
  And статус заказа должен быть "Обрабатывается" в моей учётной записи
  And инвентарь должен уменьшиться на 1 для "Sony WH-1000XM4"
  And платёж должен быть обработан на сумму $349.99

Scenario: Обработка товара вне склада
  Given продукт "Limited Edition Watch" имеет 0 в инвентаре
  When я пытаюсь добавить его в корзину
  Then я должен увидеть уведомление "Нет в Наличии"
  And кнопка "Добавить в Корзину" должна быть отключена
  And я должен увидеть опцию "Уведомить меня когда будет доступно"

Scenario: Невалидная информация о платеже
  Given у меня есть товары в корзине
  When я перехожу к оформлению заказа
  And ввожу невалидный номер кредитной карты "1234 5678 9012 3456"
  And подтверждаю заказ
  Then я должен увидеть ошибку "Неверная информация о платеже"
  And заказ не должен быть создан
  And инвентарь не должен уменьшиться

Пример Performance Testing

# Пример: Load testing для валидации производительности системы
from locust import HttpUser, task, between

class EcommerceUser(HttpUser):
    wait_time = between(1, 3)  # Ждать 1-3 секунды между запросами

    def on_start(self):
        """Login когда пользователь начинает"""
        response = self.client.post("/api/auth/login", json={
            "email": "loadtest@example.com",
            "password": "TestPass123!"
        })
        self.token = response.json()["token"]

    @task(3)
    def browse_products(self):
        """Просматривать продукты (высокая частота)"""
        self.client.get("/api/products", headers={
            "Authorization": f"Bearer {self.token}"
        })

    @task(2)
    def view_product_detail(self):
        """Смотреть конкретный продукт (средняя частота)"""
        self.client.get("/api/products/12345", headers={
            "Authorization": f"Bearer {self.token}"
        })

    @task(1)
    def add_to_cart(self):
        """Добавить продукт в корзину (меньшая частота)"""
        self.client.post("/api/cart/items", json={
            "productId": "12345",
            "quantity": 1
        }, headers={
            "Authorization": f"Bearer {self.token}"
        })

    @task(1)
    def view_cart(self):
        """Просмотреть корзину покупок"""
        self.client.get("/api/cart", headers={
            "Authorization": f"Bearer {self.token}"
        })

# Запустить с: locust -f system_load_test.py --users 1000 --spawn-rate 50
# Тестирует систему с 1000 одновременных пользователей, увеличивая 50 пользователей в секунду

Типичные Найденные Дефекты

  • Функциональные требования не выполнены
  • Ошибки бизнес-логики
  • Сбои workflow end-to-end
  • Узкие места производительности
  • Уязвимости безопасности
  • Проблемы совместимости между браузерами/устройствами
  • Неправильные сообщения об ошибках или обработка
  • Проблемы целостности данных

Лучшие Практики

  1. Тестировать в продакшн-подобном окружении: Соответствовать архитектуре, объёмам данных
  2. Использовать реалистичные тестовые данные: Представлять реальные паттерны использования
  3. Автоматизировать регрессионные тесты: Основная функциональность должна быть автоматизирована
  4. Тестировать нефункциональные требования: Производительность, безопасность, масштабируемость
  5. Документировать результаты тестирования: Всесторонняя отчётность для заинтересованных сторон
  6. Приоритизировать по риску: Фокусироваться сначала на критических бизнес-потоках

User Acceptance Testing (UAT)

UAT валидирует, что система соответствует бизнес-требованиям и готова к развёртыванию. Реальные пользователи тестируют в реалистичных сценариях.

Цели

  • Проверить, что система соответствует бизнес-потребностям
  • Валидировать юзабилити с перспективы конечного пользователя
  • Убедиться, что система готова к продакшн развёртыванию
  • Обрести уверенность заинтересованных сторон
  • Выявить пробелы между требованиями и реализацией

База Тестирования

  • Бизнес-требования и процессы
  • Пользовательские требования и user stories
  • Пользовательские руководства и материалы для обучения
  • Рабочие потоки бизнес-процессов
  • Критерии приемки

Кто Выполняет UAT?

Бизнес-пользователи, product owners, заинтересованные стороны—люди, которые на самом деле будут использовать систему.

Типы UAT

Alpha Testing:

  • Выполняется внутренним персоналом (не командой разработки)
  • Проводится в окружении разработки
  • Ранняя обратная связь перед внешними пользователями

Beta Testing:

  • Выполняется реальными конечными пользователями
  • Проводится в продакшн или близком к продакшн окружении
  • Сценарии реального использования
  • Собранная обратная связь для финальных улучшений

Contract Acceptance Testing:

  • Валидирует соответствие системы спецификациям контракта
  • Часто юридически обязывающее
  • Выполняется перед оплатой/поставкой

Operational Acceptance Testing:

  • Валидирует готовность системы к эксплуатации
  • Тестирует backup/восстановление, поддерживаемость, безопасность
  • Часто выполняется командой эксплуатации

Пример: UAT Test Case

## UAT Test Case: Генерация Месячного Финансового Отчёта

**Test ID:** UAT-FIN-001
**Feature:** Финансовые Отчёты
**Приоритет:** Высокий
**Тестировщик:** Jane Smith (Менеджер по Финансам)
**Дата:** 2025-10-02

### Бизнес-Требование
Менеджерам по финансам необходимо генерировать всесторонние месячные финансовые отчёты,
которые обобщают доходы, расходы и маржу прибыли по департаментам.

### Предусловия
- Пользователь имеет роль Менеджера по Финансам
- Финансовые данные существуют для Сентября 2025
- Пользователь аутентифицирован в системе

### Шаги Тестирования

1. Перейти к Отчёты → Финансовые → Месячная Сводка
   - **Ожидается:** Страница Месячной Сводки загружается в течение 3 секунд
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

2. Выбрать "Сентябрь 2025" из выпадающего списка месяца
   - **Ожидается:** Месяц выбран, форма обновляется
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

3. Выбрать "Все Департаменты" или конкретный департамент
   - **Ожидается:** Выбор департамента доступен
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

4. Нажать кнопку "Сгенерировать Отчёт"
   - **Ожидается:** Отчёт генерируется в течение 10 секунд, показывается индикатор прогресса
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

5. Проверить содержимое отчёта на точность
   - **Ожидается:** Отчёт включает:
     - Общий доход по департаментам
     - Общие расходы по категориям
     - Расчёты маржи прибыли
     - Сравнение месяц к месяцу
     - Визуальные графики (тренд дохода, разбивка расходов)
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

6. Проверить, что числа отчёта соответствуют исходным данным
   - **Ожидается:** Вручную проверить, что 3 примера транзакций отображаются правильно
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

7. Экспортировать отчёт как PDF
   - **Ожидается:** PDF скачивается, сохраняет форматирование, включает все данные
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

8. Экспортировать отчёт как Excel
   - **Ожидается:** Excel скачивается, данные редактируемы, формулы включены
   - **Факт:** _______________
   - **Пройдено/Провалено:** ___________

### Критерии Бизнес Приемки
- [  ] Отчёт точно отражает финансовые данные
- [  ] Отчёт генерируется в приемлемое время (< 10 секунд)
- [  ] Формат отчёта соответствует бизнес-стандартам
- [  ] Функциональность экспорта работает для PDF и Excel
- [  ] Отчёт понятен без технических знаний

### Комментарии/Issues
_____________________________________________________________________
_____________________________________________________________________

### Общий Результат: ПРОЙДЕНО / ПРОВАЛЕНО / УСЛОВНО ПРОЙДЕНО

**Подписано:** _____________________ **Дата:** __________

Типичные Найденные Дефекты

  • Требования неверно поняты командой разработки
  • Проблемы юзабилити (запутанный UI, непонятные сообщения)
  • Отсутствующие бизнес-правила или граничные случаи
  • Неправильные вычисления или бизнес-логика
  • Отчёты не соответствуют ожидаемому формату
  • Workflow не соответствует реальному бизнес-процессу
  • Проблемы производительности с реалистичными объёмами данных

Лучшие Практики

  1. Вовлекать реальных пользователей: Не посредников или QA, притворяющихся пользователями
  2. Использовать реалистичные данные: Маскировать продакшн данные при необходимости
  3. Документировать ясно: Использовать нетехнический язык
  4. Планировать достаточное время: Не спешить с фазой UAT
  5. Определять чёткие критерии приемки: Бинарное пройдено/провалено на требование
  6. Обеспечивать обучение: Помогать пользователям понимать, что тестировать
  7. Собирать обратную связь: Помимо пройдено/провалено, собирать предложения по улучшению

Сравнение Уровней Тестирования

АспектUnitIntegrationSystemUAT
ВыполняетсяРазработчикиРазработчики/QAКоманда QAБизнес-Пользователи
ФокусИндивидуальные компонентыВзаимодействия компонентовПолная системаБизнес-требования
ОкружениеМашина разработчикаТестовое окружениеStaging/TestПодобное продакшн
База ТестированияКод, docs дизайнаSpecs интерфейсаТребованияБизнес-потребности
АвтоматизацияВысоко автоматизированоВ основном автоматизированоЧастично автоматизированоВ основном ручное
Скорость ВыполненияМиллисекундыСекундыМинутыЧасы/Дни
КогдаВо время кодированияПосле unit testingПосле integrationПеред релизом
Типичные ДефектыЛогические ошибкиПроблемы интерфейсаБаги end-to-endПробелы требований

Стратегия Уровней Тестирования

Стоимость Дефектов по Уровням

Дефект найден на:  | Стоимость исправления
-------------------|----------------------
Unit Testing       | $           (1x)
Integration Testing| $$          (10x)
System Testing     | $$$         (100x)
UAT                | $$$$        (1000x)
Продакшн           | $$$$$       (10,000x)

Ключевой Принцип: Находить дефекты как можно раньше.

Распределение Тестового Покрытия

Следовать пирамиде тестирования:

        /\
       /  \      UAT (Ручное)
      / 10%\     - Критические пользовательские пути
     /------\    - Валидация бизнеса
    /        \
   /   30%    \  System Tests (В Основном Автоматизированы)
  /    Tests   \ - Сценарии end-to-end
 /--------------\- Нефункциональное тестирование
/                \
/    60% Unit     \ Unit & Integration Tests (Полностью Автоматизированы)
/   & Integration  \- Быстрые, надёжные, обширное покрытие
/____Tests_________\

Заключение

Понимание уровней тестирования позволяет командам:

  • Структурировать активности тестирования логически в течение жизненного цикла разработки
  • Распределять ответственность соответствующим образом между разработчиками, QA и бизнес-пользователями
  • Оптимизировать обнаружение дефектов, обнаруживая проблемы на самой ранней и дешёвой стадии
  • Балансировать ручное и автоматизированное тестирование на разных уровнях
  • Обеспечивать всестороннее покрытие от индивидуальных компонентов до полных бизнес-рабочих потоков

Каждый уровень тестирования служит отдельной цели. Успех достигается выполнением всех уровней соответствующим образом, с надлежащим тестовым покрытием, чёткими ответственностями и акцентом на раннее обнаружение дефектов через unit и integration testing при валидации бизнес-ценности через system testing и UAT. Для получения дополнительной информации о функциональном тестировании на этих уровнях, смотрите наше полное руководство по функциональному тестированию.