Чтобы проверить систему, изолируйте побочные эффекты

Устранение побочных эффектов - один из лучших способов создания тестируемого кода.

Картина боксерского поединка между двумя бойцами мужского пола. Их лица находятся за рамкой. Истребитель слева направил левый крюк к истребителю справа. Боец справа одет в красный шорт с маленьким символом Советского Союза.

Nock - известная библиотека, написанная на JavaScript, полезная для блокировки сетевых запросов. Он возвращает статический ответ для тестов, чтобы они могли работать, даже если HTTP-сервер недоступен.

Тем не менее, это также запах.

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

Вот почему

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

Диаграмма, на которой слева показан блок с надписью «создать список сообщений». Одна стрелка указывает на блок с надписью «сообщения ответа». Другая стрелка указывает на блок с надписью «извлечь». Блок с заголовком «выборка» имеет одну стрелку, которая указывает в направлении блока с заголовком «HTTP-сервер». Последняя стрелка прерывается надписью «Nocked».

Код имеет достойное покрытие. Однако с этим есть некоторые проблемы.

Если вы вносите изменения в тип содержимого ответа, вам необходимо изменить тесты, даже если поведение кода остается прежним:

То же самое применимо, если вы вносите изменения в URL-адрес, заголовки или параметры, которые записывает Nock. Вы должны изменить тесты, даже если поведение системы остается прежним:

Функция «создать список сообщений» - это тестируемая система (SUT). Данные из HTTP-вызова являются источником данных.

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

Диаграмма, которая показывает блок слева с заголовком «список заголовков сообщений». Одна стрелка указывает на блок с заголовком «Источник данных в памяти». Другая стрелка указывает на блок с заголовком «Данные сервера HTTP». Источник.

В тестовой среде вы можете ввести «Источник данных в памяти». Для производства вы можете использовать «Источник данных HTTP-сервера».

«Общий интерфейс» в предыдущем JSFiddle - это метод «найти заголовок сообщения». Независимо от того, как вы строите интерфейс, вы можете контролировать всех вызывающих. Поэтому изменения просты. Мартин Фаулер называет это «неопубликованным интерфейсом».

С другой стороны, если сервер нарушает контракт своего опубликованного интерфейса, скажем, атрибут класса изменяется с post-title на article-title, вам нужно только изменить реализацию Data Source. Вам не нужно вносить изменения везде.

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

С новым дизайном вы отсоединили источник данных от тестируемой системы. Следовательно, вы можете удалить Nock.

Новый дизайн также сокращает объем работы, необходимой для добавления нового правила в систему без копирования / вставки:

Тем не менее, «источник данных HTTP-сервера» имеет некоторую непроверенную логику внутри частной функции «запрашивать заголовок сообщения из html».

Чтобы проверить это, вы можете повторить тот же шаблон. Выдвиньте побочные эффекты и включите механизм «получения запроса» в «Источник данных HTTP-сервера». Таким образом, вы все еще можете протестировать код без необходимости Nock:

Поскольку у вас уже есть тесты, чтобы подтвердить, что «список заголовков сообщений» работает с «источником данных в памяти», вы можете решить проверить источник данных изолированно, чтобы убедиться, что он возвращает правильный результат:

Вы полностью вытеснили побочный эффект из логики. В этом случае реальная функция «получить запрос» является побочным эффектом. Теперь вы можете использовать Nock, чтобы покрыть это.

Однако, учитывая, что логика внутри «запроса получения» тривиальна, а Nock требует значительных затрат, имеет смысл иметь небольшое количество интеграционных тестов, которые могут выполнять все приложение, включая побочные эффекты. Вы можете использовать Nock, чтобы избежать подключения к работающему серверу, и, тем не менее, использовать HTTP-запросы, чтобы проверить, возвращает ли приложение разумный ответ, когда все части соответствуют друг другу.

Nock полезен для заглушки соединения на уровне HTTP и для предоставления статического ответа. Тем не менее, используйте его экономно. Для каждого теста вы увеличиваете значительную связь и стоимость изменений.

Если не использовать экономно, Nock может создать Nock Hell.

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

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

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

Это должно дать вам достаточно уверенности, чтобы вносить изменения, а не ломать вещи.

Присоединяйся к бою, столкнись с побочными эффектами, а затем ... убери это.

Спасибо за чтение. Если у вас есть отзывы, свяжитесь со мной в Twitter, Facebook или Github.

Спасибо Эдуардо Сломпо и Гильерме Дж. Трамонтина за их проницательные отзывы на этот пост.