В отличие от множества платформ Java страдает от недостатка библиотек заглушек соединений. Если вы давно в этом мире, то наверняка должны быть знакомы с WireMock, Betamax или даже Spock. Многие разработчики в тестах используют Mockito для описания поведения объектов, DataJpaTest с локальной h2 базой данных, Cucumber тесты. Сегодня вы познакомитесь с легковесной альтернативной, которая поможет справиться с разными проблемами, с которыми вы могли сталкиваться используя эти подходы. В частности, anyStub пытается решить следующие проблемы: AnyStub оборачивает вызовы функций, пытается найти совпадающие вызовы, которые были уже записанны. Две вещи могут произойти при этом: Из коробки anyStub предоставляет обертки для http-клиента из Apache HttpClient для создания заглушек http запросов и несколько интерфейсов из javax.sql.* для DB-соединений. Также вам предоставляется API для создания заглушек других соединений. AnyStub это простая библиотека классов и не требует специальной настройки вашего окружения. Эта библиотека нацелена на работу со spring-boot приложениями и вы получите максимум выгоды следуя этому пути. Вы можете исползовать её вне Spring, в plain Java приложениях, но определённо вам придется выполнить дополнительную работу. Дальнейшее описание ориентировано на тестирование spring-boot приложений. Давайте посмотрим на интеграционное тестирование. Это наиболее захватывающий и всесторонний способ протестировать вашу систему. На самом деле, spring-boot и JUnit делают почти всё за вас когда вы напишите магические аннотации: В настоящее время интеграционные тесты недооценены и используются ограничено, а некоторые разработчики избегают их. В основном это происходит из-за трудоёмкой подготовки и сопровождения тестов или необходимости специальной конфигурации окружения на билд-серверах. С anyStub вам не надо калечить spring-contex. Вместо этого, сохранение контекста близким к production конфигурации простое и прямолинейное.  В этом примере мы посмотрим, как подключить anyStub к Consuming a RESTful Web Service из руководства от Pivotal. Подключение библиотеки через pom.xml Следующий шаг — это модификация spring контекста.  Эта модификация не меняет взаимосвязей компонентов в приложении, а только заменяет реализацию одного интерфейса. Это отправляет нас к Принципу подстановки Барбары Лисков. Если дизайн вашего приложения его не нарушает, то и эта подстановка не нарушит функциональность.  Всё готово. Этот проект уже включает тест. Этот тест пустой, но он уже запускает контекст приложения. Самое интересное начинается здесь. Как мы говорили выше, контекст приложения в тесте совпадает с рабочим контекстом, в котором создается CommandLineRunner, в котором выполняется http-запрос к внешней системе.  Это достаточно для демонстрации работы библиотеки. После первого запуска тестов вы найдете новый файл complete/src/test/resources/anystub/stub.yml. Что произошло? spring-boot встроил RestTemplateBuilder из тестовой конфигурации в приложение. Это привело к работе приложения через stub-реализацию http-клиента. StubHttpClient перехватил запрос, не нашел файл заглушки, выполнил запрос, сохранил результат в файле и вернул восстановленный из файла результат. С этого момента, вы можете запускать этот тест без интернет подключения и этот запрос будет выполнятся успешно. restTemplate.getForObject() будет возвращать одинаковый результат. Вы можете полагаться на этот факт в ваших будущих тестах.  Все описанные изменения вы можете найти на GitHub. На самом деле мы все еще не создали ни одного теста. Перед написанием тестов давайте посмотрим как это работает с базами данных. В этом примере мы добавим интеграционной тест к Accessing Relational Data using JDBC with Spring из руководства от Pivotal. Тестовая конфигурация для этого случая выглядит так: Здесь создается обычный datasource к внешней базе данных и оборачивается stub-реализацией — класс StubDataSource. Spring-boot встраивает его в контекст. Ещё нам необходимо создать хотя бы один тест для запуска spring-контекста в тесте. Это опять пустой тест — его единственная задача запустить контекст приложения. Здесь мы видим очень важную аннотацию @AnystubId, но она пока не будет задействована. После первого запуска вы найдете новый файл src/test/resources/anystub/stub.yml, который включает все обращения к базе данных. Вы будете удивлены как spring работает за сценой с базами данных. Отметим что, новые запуски теста не приведут к реальному обращению к базе данных. Если вы удалите test.mv.db он не появится после повторных запусков тестов. Полный набор изменений можно посмотреть на GitHub. Подведем итоги. с anyStub: Вероятно, у вас есть вопросы: как это покрывает случаи, когда база данных еще не существует, что делать с негативным тестированием и обработкой исключений. Мы вернемся к этому, но сначала разберемся с написанием простых тестов. Сейчас мы по-экспериментируем над Consuming a RESTful Web Service. Этот проект не содержит компонент, которые могут быть протестированы. Ниже создаются два класса, которые должны изобразить два слоя какого-то дизайна архитектуры. DataProvider обеспечивает доступ к данным в изменчивой внешней системе. DataProcessor выполнят обработку данных из внешней системы. Мы намерены протестировать DataProcessor. Необходимо протестировать корректность алгоритма обработки и защитить систему от деградации от изменений в будущем. Чтобы достичь этих целей вы можете подумать о создании мок-объекта DataProvider с набором данных и его передачи в конструктор DataProcessor в тестах. Другой способ может состоять в декомпозиции DataProcessor, чтобы выделить алгоритм обработки класса Quote. Затем, такой класс легко протестировать с помощью юнит-тестов (Наверняка, это рекомендуемый способ в уважаемых книжках про чистый код). Попробуем избежать изменений кода и изобретения тестовых данных и просто напишем тест. Время рассказать об аннотации @AnystubId. Эта аннотация помогает управлять и контролировать stub-файлы в тестах. Её можно использовать с тест-классом или его методом. Эта аннотация устанавливает индивидуальный stub-файл для соответствующей области. Если какая-то область одновременно покрыта аннотациями уровня класса и метода — аннотация метода имеет приоритет. Эта аннотация имеет пераметр filename, который определяет имя stub-файла. расширение ".yml" добавляется автоматически, если пропущено. Запустив этот тест вы не обнаружите новый файл. Файл src/test/resources/anystub/stub.yml уже был создан ранее и этот тест его переиспользует. Число 131 мы получили из этой заглушки, проанализировав результат запроса. В этом тесте аннотация @AnyStubId появляется без параметра filename. В этом случае используется файл src/test/resources/anystubprocessDataTest2.yml. Имя файла строится из имени функции (класса) + ".yml". Раз anyStub создает новый файл для этого теста необходимо выполнить реальный вызов системы. И это наша удача, что новая цитата имеет такую-же длину. Две последних проверки показывают как проверить поведение приложения. Вам доступно: выбор запросов по параметрам или части параметров и подсчет количества запросов. Есть несколько вариантов функций times and match, с которыми можно ознакомиться в документации. В этом тесте @AnyStubId появляется с новым параметром requestMode. Он позволяет управлять разрешениями дла stub-файлов. Существует два аспекта для управления: поиск по файлу и разрешение на вызов внешней системы. RequestMode.rmTrack устанавливает следующие правила: если файл только что создан все запросы отправляются во внешнюю систему и записываются в файл вместе с ответами не зависимо от того есть ли в файле идентичный запрос (дубликаты в файле допускаются). Если перед запуском тестов stub-файл существует запросы во внешнюю систему запрещены. Вызовы ожидаются точно в той же последовательности. Если очередной запрос не совпадает с запросом в файле, генерируется исключение. RequestMode.rmNew этот режим активируется по-умолчанию. Каждый запрос ищется в stub-файле. Если совпадающий запрос найден — соответствующий результат восстанавливается из файла, запрос во внешнюю систему откладывается. Если запрос не найден — осуществляется запрос внешней системы, результат сохраняется в файле. Дубликаты запросов в файле — не возникают. RequestMode.rmNone Каждый запрос ищется в stub-файле. Если совпадающий запрос найден — его результат восстанавливается из файла. Если тест формирует запрос, отсутствующий в файле — то генерируется исключение. RequestMode.rmAll перед первым запросом stub-файл отчищается. Все запросы записываются в файл (дубликаты в файле допускаются). Вы можете использовать этот режим, если хотите понаблюдать за работой соединения. RequestMode.rmPassThrough все запросы напрямую отправляются во внешнюю систему обходя stub реализации. Эти изменения доступны на GitHub Мы увидели как anyStub сохраняет ответы. Если при обращении к внешней системе будет сгенерировано исключение — anyStub сохранит его, а при последующих запросах будет его воспроизводить. Часто исключения генерируются классами верхнего уровня, при этом классы соединений получают допустимый ответ (Вероятно, с кодом ошибки). В этом случае anyStub ответственный за воспроизведение того самого ответа с кодом ошибки, а классы верхних уровней будут так же генерировать исключения для ваших тестов. Добавляйте файлы заглушек в репоситорий. Не бойтесь удалять и перезаписывать файлы заглушек. Управляйте файлами заглушек разумно. Вы можете переиспользовать один файл в нескольких тестах или обеспечить индивидуальный файл для каждого теста. Пользуйтесь этой возможностью для ваших потребностей. Но обычно использование одного файла с разными режимами доступа — плохая идея. Это все главные возможности anyStub.