November 10, 2019

Классификация тестов

Традиционно тесты классифицируют как юнит, интеграционные, функциональные. Логика следующая:

  1. Юнит - тестирует один модуль или класс с зависимостями компиляции, и по возможности без модулей которые нужны только в рантайме. Другими словами старается тестировать поведение одной штуки в изоляции.
  2. Интеграционные - тестируют несколько модулей, примерно как они будут связаны в рантайме. Тестирует непосредственное взаимодействие нескольких штук.
  3. Функциональные - более-менее вся система как она будет работать. Тестирует опосредованное взаимодействие, очень многих штук сразу.

Мне она уже давно кажется абсолютно бесполезной.

Одна проблема с ней - размазанные границы. Многие тесты попадают в пару категорий. Вроде "в основном у нас один модуль, но вот эту зависимость не замокать". Или "вообще тест функциональный, но с настоящей БД сложно запускать в тимсити".

Вторая проблема - плохая связанность с операционными характеристиками теста. Пример: есть интеграционные-тесты на процедуру логина и на драйвер чужого сервиса. Они оба и правда интеграционные - собирают пару модулей вместе, мокают зависимости такой сборки. Первый ходит в заглушку БД и отдаёт фиктивные данные, второй получает фиктивные запросы и ходит в заглушку сервиса. При этом у них сильно отличается время работы, у первого 3±1ms у второго 30±10ms. Гонять сотню аналогов первого можно каждый Ctrl+S, сотню аналогов второго - только в перерывах на чай.

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

  1. Быстрые тесты. Пользуются только памятью и процессором, субквадратичные алгоритмы, входные данные менее 10MB.
  2. Медленные тесты. Пользуются одним внешним ресурсом (файлы, сети, видяхи :) ), субквадратичные алгоритмы, входные данные менее 100MB.
  3. Тяжёлые тесты. Пользуются многими внешними ресурсами, произвольные алгоритмы, произвольные объёмы данных.

С некоторыми исключениями про такие категории можно сказать следующее:

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

Медленные тесты займут до 5 минут для компонента, и будут иметь небольшие выбросы. Можно применить сдержанные политики таймаутов и памяти. Стоит выполнять для одного или нескольких компонентов на каждом коммите. Для всего проекта может выполнять CI.

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

Это конечно легко тюнится под конкретную кодовую базу, процессные и операционные реалии. Надеюсь идея понятна.

Кстати JUnit 5 добавил радикально улучшенную поддержку категоризации тестов.

Tags: development test