Основы модульного тестирования
Модульные тесты (Unit Tests) лежат в основе автоматизации тестирования. Они проверяют отдельные части программы в изоляции и дают быструю обратную связь, когда что-то перестаёт работать. Правильное использование модульных тестов помогает избежать дорогостоящих ошибок и создаёт уверенность в собственном коде.
Суть в нескольких строках
- Модульные тесты проверяют отдельные функции, классы или методы в изоляции.
- Они быстрые, повторяемые и дают прямую обратную связь.
- Хорошие модульные тесты независимы, детерминированы и сосредоточены на одном поведении.
- Они образуют широкую основу пирамиды тестирования.
Формальное определение
Модульный тест проверяет наименьшую тестируемую единицу программы в изоляции. Обычно функция или метод вызывается с определёнными входными данными, а результат сравнивается с ожидаемым значением. Зависимости вроде баз данных, файловых систем или внешних сервисов заменяются на заглушки или mock-объекты, так что тест проверяет исключительно логику самой единицы.
Структура модульного теста
Классический модульный тест следует паттерну Arrange-Act-Assert:
- Arrange: подготовка входных данных, объектов и зависимостей.
- Act: выполнение тестируемой функции.
- Assert: проверка результата против ожидаемого значения.
Лучшие практики
- Один тест, один сценарий: каждый тест должен проверять ровно одно поведение.
- Независимость: тесты не должны зависеть друг от друга.
- Детерминизм: одинаковые входные данные всегда должны давать одинаковый результат.
- Читаемость: названия тестов описывают поведение, а не имена методов.
- Экономно с mock-объектами: слишком много mock-объектов скрывают реальные ошибки.
- Не забывать граничные случаи: тестируйте пустые входные данные, null-значения и граничные условия.
Практический пример: расчёт цены со скидкой
import unittest
def calculate_price(base_price, discount_percent):
if base_price < 0:
raise ValueError("Preis darf nicht negativ sein")
if discount_percent < 0 or discount_percent > 100:
raise ValueError("Rabatt muss zwischen 0 und 100 liegen")
return base_price * (1 - discount_percent / 100)
class TestPriceCalculation(unittest.TestCase):
def test_no_discount(self):
self.assertEqual(calculate_price(100, 0), 100)
def test_with_discount(self):
self.assertEqual(calculate_price(100, 20), 80)
def test_full_discount(self):
self.assertEqual(calculate_price(100, 100), 0)
def test_invalid_price(self):
with self.assertRaises(ValueError):
calculate_price(-10, 10)
def test_invalid_discount(self):
with self.assertRaises(ValueError):
calculate_price(100, 110)
if __name__ == '__main__':
unittest.main()
Преимущества и недостатки
Преимущества
- Быстрая обратная связь: модульные тесты выполняются за миллисекунды или несколько секунд.
- Локализация ошибок: проблемы сразу же указывают на конкретную единицу.
- Безопасность при рефакторинге: существующие тесты показывают, не сломано ли что-то при изменениях.
- Лучшая структура кода: код, который легко тестировать, обычно лучше модуляризирован.
- Документация: тесты показывают, как предполагается использовать отдельную единицу.
Недостатки
- Затраты на начальном этапе: тесты нужно писать и поддерживать.
- Не всё поддаётся покрытию: сложная логика UI или внешние системы трудно тестировать в изоляции.
- Ложная уверенность: высокое покрытие кода ничего не говорит о качестве тестов.
- Обслуживание тестов: плохо написанные модульные тесты замедляют разработку.
Ключевые моменты для подготовки
- Определение и назначение модульных тестов.
- Паттерн Arrange-Act-Assert.
- Разница между модульными и интеграционными тестами.
- Преимущества и границы применения модульных тестов.
- Распространённые фреймворки: JUnit, NUnit, pytest, Jest.
Типичные вопросы на экзамене (с кратким ответом)
-
Что такое модульный тест? Тест, проверяющий наименьшую тестируемую единицу в изоляции.
-
Какой паттерн часто используется для модульных тестов? Arrange-Act-Assert.
-
Что такое mock-объект? Объект-заменитель для зависимости, который моделирует контролируемое поведение.
-
Почему модульные тесты должны быть детерминированными? Чтобы одинаковые входные данные всегда давали одинаковый результат и тесты были надёжными.
-
Назовите одно преимущество модульных тестов при рефакторинге. Они сразу показывают, не нарушена ли существующая функциональность в результате изменений.
Следующий шаг в пути обучения Software Testing
Следующая статья в этом разделе посвящена Основам интеграционного тестирования и рассматривает, как проверяется взаимодействие нескольких компонентов.
Основные источники
- https://martinfowler.com/bliki/UnitTest.html
- https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html
- https://en.wikipedia.org/wiki/Unit_testing



