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

Тестирование для разработчика

Разработчику Тестировщику

Отладка и тестирование

Отладка (4.14 / Отладка) находит и устраняет конкретный дефект в текущем запуске программы. Тестирование фиксирует ожидание: при таких входных данных программа всегда ведёт себя определённым образом — и ловит регрессии, когда вы или коллега меняете код позже.

Регрессия — возврат старой ошибки после нового изменения.

Без автоматических тестов каждый фикс проверяют вручную. С тестами одна команда (pytest, npm test, dotnet test) перед commit или в CI (Continuous Integration — автоматические проверки на сервере) подтверждает, что ничего не сломалось.

Полный маршрут QA, нагрузка, безопасность — 7.05 Тестирование. Ниже — минимум, который полезен каждому разработчику.


Виды проверок

ВидКтоЧто проверяетГде углубиться
Unit (модульный)РазработчикОдна функция или класс изолированно7.05
Integration (интеграционный)Разработчик / QAНесколько модулей, БД, HTTP7.05
E2E (сквозной)QA / автоматизацияСценарий "как пользователь"7.05
РучноеQA, разработчикНовый UI, исследование7.05 intro
НагрузочноеDevOps / QAПоведение под нагрузкой (RPS — запросов в секунду)7.05 / 1014

Разработчик в первую очередь пишет unit- и часть integration-тестов; E2E часто ведёт отдельная роль или CI.


Unit-тест — структура

Классическая схема Arrange – Act – Assert (подготовка — действие — проверка):

ПСЕВДОКОД
// Arrange — подготовка
a ← 2
b ← 3

// Act — действие
result ← sum(a, b)

// Assert — проверка
если result ≠ 5
ошибка "ожидали 5"

Хороший unit-тест:

  • быстрый — миллисекунды, без сети и БД, если возможно;
  • детерминированный — один и тот же результат при каждом запуске;
  • одна причина падения — одна логическая проверка или связанный блок.

Зависимости подменяют заглушками (mock — имитация объекта; stub — заготовленный ответ) — Dependency Injection.


Red – Green – Refactor

Цикл экстремального программирования (TDD — Test-Driven Development, разработка через тесты):

  1. Red — написать тест, который падает (функции ещё нет или поведение неверное).
  2. Green — минимальный код, чтобы тест прошёл.
  3. Refactor — улучшить структуру без смены поведения; тесты страхуют — Рефакторинг.

Тест до кода дисциплинирует API: вы проектируете, как вызывать функцию, а не подстраиваете интерфейс под случайную реализацию.


Инструменты по стеку

СтекФреймворкЗапуск
Pythonpytest, unittestpytest
JavaScript / TSJest, Vitestnpm test
JavaJUnitMaven/Gradle test
C#xUnit, NUnitdotnet test
Gotesting + testifygo test ./...

Примеры ASP.NET — Тесты ASP.NET Core, WPF-практикум — шаг 5.


Что тестировать в первую очередь

  1. Бизнес-правила — скидка, валидация, расчёт суммы.
  2. Граничные случаи — пустой ввод, ноль, максимум, null.
  3. Регрессии — баг, который уже чинили: тест не даст ошибке вернуться.
  4. Публичный API модуля — контракт для остального кода.

Покрытие кода (coverage) — процент строк, которые выполнили тесты. Цель — покрыть риск и сложность, а не гнаться за 100% ради цифры.


Тесты и Git / CI

Перед merge в main CI запускает тесты автоматически — GitHub Actions — рецепты. Локально полезна привычка:

# перед push — примеры; команды зависят от проекта
pytest
npm test
dotnet test

Падающий тест в CI блокирует merge — это штатная защита качества, а не препятствие.


Пирамида тестирования

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

  • Unit — основание: быстро, много, изолируют логику.
  • Integration — середина: БД, HTTP, несколько модулей.
  • E2E — вершина: полный сценарий в браузере; дорого поддерживать.

Новичку достаточно 3–10 unit-тестов на pet-проект — 114 pet-проект. E2E подключайте, когда есть стабильный UI — 7.05 / 119.


Пример unit-теста на Python (pytest)

Функция:

def discount(price: float, percent: int) -> float:
if percent < 0 or percent > 100:
raise ValueError("percent must be 0..100")
return price * (100 - percent) / 100

Тест test_discount.py:

import pytest
from pricing import discount

def test_no_discount():
assert discount(100, 0) == 100

def test_half_price():
assert discount(200, 50) == 100

def test_invalid_percent():
with pytest.raises(ValueError):
discount(100, -1)

Запуск:

pip install pytest
pytest -v

assert — проверка "ожидали X, получили Y". pytest.raises — ожидаем исключение.


Пример unit-теста на JavaScript (Jest)

// sum.js
function sum(a, b) {
return a + b;
}
module.exports = { sum };

// sum.test.js
const { sum } = require('./sum');

test('adds 2 + 3', () => {
expect(sum(2, 3)).toBe(5);
});

test('adds negative', () => {
expect(sum(-1, 1)).toBe(0);
});
npm init -y
npm install --save-dev jest
# в package.json: "scripts": { "test": "jest" }
npm test

Фикстуры (fixtures)

Фикстура — подготовка данных или окружения перед тестом, общая для нескольких тестов.

import pytest

@pytest.fixture
def sample_user():
return {"id": 1, "name": "Anna", "role": "user"}

def test_user_name(sample_user):
assert sample_user["name"] == "Anna"

Фикстура создаёт свежий объект для каждого теста — тесты не портят друг другу данные.


Параметризация — один тест, много входов

@pytest.mark.parametrize("price,percent,expected", [
(100, 0, 100),
(100, 10, 90),
(50, 50, 25),
])
def test_discount_cases(price, percent, expected):
assert discount(price, percent) == expected

Три сценария — три строки в отчёте pytest без копипасты.


Mock и stub — подробнее

ТерминРоль
StubОтдаёт заранее заданный ответ ("БД вернула user_1")
MockЗаписывает, как вызывали ("метод save вызван 1 раз")
FakeУпрощённая реализация (in-memory БД вместо PostgreSQL)

Зачем: unit-тест не должен ходить в реальную почту или prod-БД. Подменяете зависимость — Dependency Injection.

from unittest.mock import Mock

def test_send_welcome(mocker):
mailer = Mock()
register_user(mailer, "a@test.com")
mailer.send.assert_called_once()

Integration-тест — пример

Проверка HTTP API с тестовой БД:

def test_create_task(client, db):
response = client.post("/api/tasks", json={"title": "Test"})
assert response.status_code == 201
assert db.query(Task).count() == 1

client — тестовый HTTP-клиент фреймворка (FastAPI TestClient, Django test client). БД — отдельная test- база или SQLite in-memory.


Именование тестов

Хорошее имя описывает сценарий:

  • test_discount_returns_zero_when_price_is_zero
  • test_login_fails_with_wrong_password

Плохое:

  • test1, test_discount, works.

При падении в CI имя — первое, что читает разработчик.


Flaky test — "плавающий" тест

Flaky test — иногда зелёный, иногда красный без изменения кода. Причины:

  • зависимость от времени (sleep, deadline);
  • порядок тестов (общая глобальная переменная);
  • сеть или внешний API;
  • race condition в многопоточном коде.

Лечение: убрать sleep, фиксировать seed random, мокать сеть, изолировать тесты — Как искать баг.


Покрытие кода (coverage)

pytest --cov=myapp --cov-report=term-missing

Показывает строки, которые не выполнились в тестах. Полезно найти "дыры", но 100% coverage ≠ нет багов — можно вызвать строку, не проверив результат.


CI — минимальный pipeline

# фрагмент .github/workflows/test.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements.txt
- run: pytest

Полные рецепты — Lab / 1134, 8.04 DevOps.


Чек-лист перед merge

  • Локально pytest / npm test зелёные
  • Новое поведение покрыто тестом
  • Нет @pytest.mark.skip без причины
  • CI на PR зелёный
  • Ревьюер видел тесты в diff — 117 PR

Когда тест писать не обязательно (но осторожно)

  • одноразовый скрипт миграции;
  • прототип на выброс;
  • чистый UI-layout без логики.

В prod-коде с бизнес-правилами тесты ожидаются командой.


Отладка и тест — сравнение

ОтладкаТест
КогдаБаг уже проявилсяДо или после фикса
РезультатПонимание + патчАвтоматическая проверка
ПовторяемостьРучнаяСкрипт в CI
ИнструментBreakpoint, логpytest, Jest, …

Идеальный цикл: воспроизвели баг → написали падающий тест → починили → тест зелёный → commit. Поиск бага — Как искать баг.


Связь с другими темами

  • Рефакторинг без тестов рискован — 612.
  • Легаси — characterization tests — 7.11.
  • Pet-проект — "3–5 тестов" в чек-листе — 114.
  • Конструирование ПО (SDLC) — 7.12.
  • Код-ревью — ревьюер смотрит, есть ли тесты — 117.

Что дальше

Углубление — весь раздел 7.05 Тестирование: виды QA, API-тесты, автоматизация, нагрузка, тестирование ИБ.