Лучшее руководство для выбора стратегии тестирования программного обеспечения
Один из методов разработки программного обеспечения, против которого я выступаю, - это пирамида тестирования программного обеспечения. Рекомендация примерно такая: «Делайте больше изолированных тестов и меньше интеграционных тестов»:
Напишите множество небольших и быстрых модульных тестов. Напишите еще несколько грубозернистых и очень мало высокоуровневых тестов, которые тестируют ваше приложение от начала до конца.
- Пирамида практических тестов (2018)
Существенным моментом (пирамиды тестов) является то, что у вас должно быть намного больше низкоуровневых UnitTests, чем высокоуровневых BroadStackTests, выполняемых через графический интерфейс.
- TestPyramid (2012)

Такую рекомендацию не следует применять вслепую в каждой ситуации. Как и все остальное, ответ должен быть «в зависимости от обстоятельств». В этой статье я хотел бы сформулировать часть «зависит от обстоятельств».
Спектр
В зависимости от того, какую диаграмму пирамиды теста вы видите, вы найдете несколько типов тестов: модульный, интеграционный, пользовательский интерфейс, сквозной, ручной, приемочный и т. Д. Они довольно запутаны и иногда перекрываются (например, UI и конец сквозные тесты же?).
Что мне кажется более полезным, если подумать о тестах, так это представить их в спектре количества задействованных компонентов, где компонент определяется как наименьшая тестируемая единица (т. е. функция).

Слева в тесте задействован только один компонент. Мы тестируем только одну функцию изолированно. Это соответствует нижней части тестовой пирамиды.

Справа показаны все компоненты тестируемой системы. Здесь мы тестируем систему от начала до конца, даже если для этого настроена сторонняя система. Это соответствует верхней части тестовой пирамиды.

В середине мы тестируем подмножество системы, которое включает более одного компонента. Примером теста в этой категории может быть тест на основе API. Это соответствует средней части тестовой пирамиды.

Размеры
По той же оси мы можем добавить больше измерений, которые описывают тенденцию характеристик тестов:
- Уверенность: как прохождение тестов гарантирует, что функция работает. Чем больше компонентов задействовано, тем увереннее мы находимся в тесте, и наоборот. (Обязательная единица vs анекдот интеграционного теста)
- Отток тестов: насколько вероятно изменение существующих тестов при изменении существующих кодов. Чем больше задействовано компонентов, тем меньше вероятность того, что вам потребуется изменить существующие тесты. Это потому, что вы склонны тестировать потоки более высокого уровня. Изменение деталей реализации не имеет значения. Напротив, если вы пишете тест для каждой функции, вам может потребоваться изменить существующие тесты для каждого рефакторинга.
- Стоимость выполнения: стоимость выполнения теста. Стоимостью здесь могут быть системные ресурсы, время или даже деньги. Стоимость, как правило, выше, когда задействовано больше компонентов, из-за более длительного времени запуска теста и необходимости выделения большего количества ЦП / ОЗУ / диска.
- Настройка вариантов случая: насколько сложно настроить тесты, охватывающие все различные случаи. Чем меньше задействовано компонентов, тем проще настраивать различные кейсы (например, с помощью моков). С другой стороны, чем больше задействовано компонентов, тем сложнее настраивать различные кейсы. Например, если тестовый пример - «должен возвращать X, когда компонент A выдает ошибку», может быть сложно воспроизвести среду, в которой компонент A выдаст ошибку.
- Выявление проблемы: насколько сложно выявить ошибки, которые приводят к сбою теста. Чем больше задействовано компонентов, тем сложнее определить, какие компоненты (а может быть и несколько!) Содержат ошибки. Если вы протестируете только один компонент, вы легко сможете определить, где находится ошибка.

Пирамида тестирования программного обеспечения рекомендует, чтобы у вас было больше тестов, которые попадают в левую часть спектра, чем в правую. Однако в Спектре тестирования программного обеспечения, где ваш тест попадает в спектр, гораздо менее важен.
Важно найти стратегию тестирования, которая максимизирует достоверность теста, сводя к минимуму отток тестов, стоимость выполнения, сложность настройки различных случаев и сложность выявления проблем.
Пример
Допустим, у нас есть приложение для электронной коммерции. Архитектурно клиент - это SPA, который общается с серверной частью через API. Среди множества его функций давайте выберем две и решим, какая стратегия тестирования им лучше всего подходит. Это две функции: регистрация пользователя и ценообразование в корзине.
Регистрация пользователя
Функция регистрации пользователя обычно проста. Пользователь регистрируется с адресом электронной почты и паролем. Если электронная почта уже занята, мы отклоняем регистрацию. Мы также выполняем проверку ввода, например, чтобы пароль был длиннее 8 символов. В противном случае мы регистрируем пользователя в системе.
Допустим, мы пишем тесты на уровне класса, то есть тесты, которые попадают в левую часть спектра. Затем давайте попробуем назначить значения для каждого параметра. Обратите внимание, что нет точной науки о присвоении таких значений. Все сводится к вашей оценке. В моем случае вот мои оценки:

У нас средний уровень уверенности, потому что, тестируя каждый класс по отдельности, мы не проверяем, правильно ли настроена «связь» между всеми этими классами. Отток тестов высок, поскольку тесты уровня класса легко нарушить рефакторинг. Однако другие параметры имеют идеальное значение.
Можем ли мы сделать лучше? Как бы выглядели размеры, если бы мы вместо этого проводили тесты на основе API?

И уверенность, и отток тестов значительно приблизились к своему идеальному положению, в то время как другие параметры лишь незначительно хуже. Стоимость запуска, например, теперь будет составлять, скажем, ~ 5 секунд вместо ~ 1,5 секунды. Выявление проблем и настройка различных тестовых сценариев также несложны, учитывая простоту этой функции.
В этом сценарии я бы пропустил тесты на уровне класса и просто применил тесты, управляемые API.
Цены на корзину
В отличие от регистрации пользователя, ценообразование в корзине намного сложнее. Многие критически важные для бизнеса случаи нуждаются в проверке. Например, расчет рекламных акций, купоны, доступность товаров и т. Д. С точки зрения реализации, одна функция ценообразования корзины может состоять из нескольких достаточно сложных функций.
Если мы выберем тот же подход к тестированию, что и функция регистрации пользователя, размеры будут выглядеть следующим образом:

Хотя показатели достоверности и оттока тестов хороши, настроенные тестовые примеры и задачи определения размеров находятся в самом худшем случае. Например, если тест не проходит из-за того, что окончательная цена снижена на 0,2 доллара, насколько легко вы узнаете, где находится ошибка?
Лучше всего сочетать тесты нижнего и верхнего уровня. В тестах нижнего уровня проверяются все различные тестовые примеры, так как на этом уровне их проще настроить. Кроме того, это значительно снизит сложность выявления проблем. Напротив, тесты более высокого уровня выполняют лишь несколько тестовых примеров. Это повышает уверенность в том, что функция работает правильно, по крайней мере, для нескольких тестовых случаев, которые мы настроили.

В конце концов, это выглядит именно так, как рекомендует Software Test Pyramid: больше изолированных тестов и меньше интеграционных тестов.
Однако я хочу подчеркнуть, что такая рекомендация не всегда наиболее применима в любой ситуации. Это имеет смысл для этой функции, но не имеет смысла для функции регистрации пользователей.
Вывод
Эта статья представляет Спектр тестирования программного обеспечения как альтернативу Пирамиде тестирования программного обеспечения для выбора правильной стратегии тестирования программного обеспечения. В то время как Software Test Pyramid рекомендует иметь больше изолированных тестов и меньше интеграционных тестов, Software Testing Spectrum рекомендует выбирать стратегию тестирования для каждого конкретного случая, учитывая ее влияние на различные аспекты.