Рубрики
Без рубрики

Тестирование устаревшего кода, часть 2: Нарушение правил

Как протестировать непроверяемое. Помечено как рефакторинг, устаревший код, java, тестирование.

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

Есть действительно хороший шанс, что вы найдете что-то вроде этого:

  • вы хотите заменить некоторую зависимость, но она не вводится, а скорее создается в коде с помощью конструктора, например SomeService SomeService(); .
  • вы хотите изменить результат вызова метода, который вызывает внешнюю службу по сети, но это статический метод, например, Какой-нибудь Сервис.fetchExternalResult(...)
  • что-нибудь еще, что нарушает все принципы дизайна, которых вы пытались придерживаться

Что вы могли бы сделать, но, вероятно, не должны

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

Я работал с некоторым загадочным кодом, где мы решили перенести несколько простых классов POJO в другой пакет. Иерархия казалась неправильной, и это выглядело как действительно простое изменение. Однако, когда мы это сделали, некоторые операции начали завершаться сбоем во время выполнения. После некоторого серьезного расследования мы обнаружили, что какая-то внешняя (но внутренняя) зависимость проверяла конкретные имена пакетов, которые затем использовались “особым образом”. Не важно, что он сделал, главное, что простое изменение, подобное этому, вызвало несколько часов расследования. Подобные вещи трудно предсказать, особенно когда рефлексия используется не по назначению.

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

Что, вероятно, является лучшим способом…

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

Почему я думаю, что это не идеально? Ну, во-первых, PowerMock – это, по сути, инструмент взлома . Если в вашем коде уже присутствуют некоторые манипуляции с байт-кодом, вам, скорее всего, потребуется скорректировать настройки. Существует постоянная проблема с инструментом покрытия кода JaCoCo . Вероятно, вам следует сначала провести исследование, прежде чем вы начнете использовать его напрямую.

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

Поэтому вы должны использовать его только тогда, когда нет другого способа .

Каков наименее болезненный способ

Как я уже упоминал в первой части, вы должны протестировать наименее неясный черный ящик. Это означает, что вы пока оставите реализацию такой, какая она есть, и перейдете к какому-нибудь компонентному/интеграционному тесту, который будет использовать входные параметры для создания альтернативных сценариев.

Существует очень высокая вероятность того, что вам нужно будет имитировать внешние зависимости. Это те, которые находятся на некоторых серверах в вашей тестовой инфраструктуре, являются общими и их действительно трудно изменить. К счастью для нас, разработчиков, почти для всего есть альтернатива. Вот лишь некоторые из примеров:

  • для HTTP-связи (REST API и т.д.) вы можете использовать Проволочный моток . Это очень гибкий инструмент, который можно использовать внутри проекта или в качестве внешнего сервера. Вы можете имитировать ответ внешнего сервера, а также настроить ожидания, которые можно проверить. Это особенно полезно, если в вашем проекте нет какой-либо платформы приложений, которая обеспечивала бы аналогичную функциональность.
  • для более сложных сценариев вы можете использовать Грузовой плагин Maven . Иногда проще просто создать отдельное приложение для имитирования ваших звонков. Если WireMock недостаточно, вы можете использовать фреймворк для быстрого прототипирования, такой как Spring Boot . Мы успешно использовали это в одном из наших проектов по рефакторингу, где мы хотели заменить уровень доступа к базе данных на REST API, который разрабатывался одновременно. Наш макет сервера представлял собой простую реализацию предлагаемого интерфейса, который просто обращался к базе данных.
  • если доступ к базе данных является проблемой, вы всегда можете использовать экземпляр какой-либо тестовой библиотеки БД в памяти, такой как H2 . Проблема возникает, когда производственный код использует определенный диалект (да, я смотрю на вас, Oracle).
  • для большинства баз данных NoSQL существует встроенный экземпляр или тестовый экземпляр, который можно использовать в качестве замены. Вы также можете найти полезные тестовые библиотеки , которые будут выполнять подготовку и проверку тестовых данных.

Итоговый результат

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

Оригинал: “https://dev.to/rapasoft/testing-legacy-code-part-2-bending-the-rules-16n1”