Spring Mock-MVC предоставляет отличные методы тестирования Spring Boot REST API. Mock-MVC позволяет нам тестировать обработку запросов Spring-MVC без запуска реального сервера. Я использовал тесты Mock-MVC в различных проектах, и по моему опыту они часто бывают довольно многословными. Это не обязательно плохо. Однако это часто требует копирования/вставки однотипных фрагментов кода в тестовые классы. В этом посте мы рассмотрим несколько способов усовершенствования тестов Spring Mock-MVC. Первый вопрос, который нам нужно задать, - это то, что мы хотим протестировать с помощью Mock-MVC. Вот некоторые примеры тестовых сценариев: Тестирование только веб-слоя и эмулирование всех зависимостей контроллера. Тестирование веб-уровня с помощью логики домена и имитация сторонних зависимостей, таких как базы данных или очереди сообщений. Тестирование полного пути от веб-слоя до базы данных с помощью замены сторонних зависимостей встроенными альтернативами, если это возможно (например, H2 или embedded-Kafka ) У всех этих сценариев есть свои плюсы и минусы. Однако я думаю, что есть два простых правила, которым мы должны следовать: Протестируйте как можно больше в стандартных JUnit тестах (без Spring). Это значительно улучшает производительность тестов и упрощает написание тестов. Выберите сценарий (-и), который вы хотите протестировать с помощью Spring, и будьте последовательны в зависимостях, которые вы имитируете. Это упрощает понимание тестов и может их ускорить. При запуске множества различных тестовых конфигураций Spring часто приходится повторно инициализировать контекст приложения, что замедляет тесты. При максимальном использовании стандартных JUnit тестов последний упомянутый выше сценарий часто подходит. После того, как мы протестировали всю логику с помощью быстрых модульных тестов, мы можем использовать несколько тестов Mock-MVC, чтобы убедиться, что все части работают вместе, от контроллера до базы данных. Spring позволяет нам объединять несколько аннотаций Spring в одну настраиваемую аннотацию. Например, мы можем создать собственную аннотацию @MockMvcTest: Теперь нашему тесту нужна только одна аннотация: Таким образом, мы можем очистить тесты от различных аннотаций. Это также полезно для стандартизации конфигурации Spring для наших тестовых сценариев. Давайте рассмотрим следующий пример запроса Mock-MVC и посмотрим, как мы можем его улучшить: Он отправляет запрос PUT с некоторыми JSON данными и заголовком авторизации в /products/42. Первое, что бросается в глаза - это фрагмент JSON в строке Java. Это, очевидно, проблема, поскольку экранирование двойных кавычек, требуемое строками Java, делает его трудно читаемым. Обычно мы должны использовать объект, который затем конвертируется в JSON. Прежде чем мы рассмотрим этот подход, стоит упомянуть текстовые блоки. Блоки Java Text были представлены в JDK 13/14 в качестве функции предварительного просмотра. Текстовые блоки - это строки, которые занимают несколько строк и не требуют экранирования двойных кавычек. С помощью текстового блока мы можем отформатировать встроенный JSON более красивым способом. Например: В определенных ситуациях это может быть полезно. Однако мы, по-прежнему, должны отдать предпочтение объектам, преобразованным в JSON вместо того, чтобы вручную писать и поддерживать строки JSON. Например: Далее мы создаем объект product и конвертируем его в JSON с помощью небольшого вспомогательного метода objectToJson(..). Это немного помогает. Тем не менее, мы можем добиться большего. В нашем запросе много элементов, которые можно сгруппировать. При создании JSON REST-API, вероятно, нам часто придется отправлять аналогичный запрос PUT. Поэтому мы создаем небольшой статический метод для быстрого доступа: Этот метод преобразует параметр body в JSON с помощью Jackson ObjectMapper . Затем он создает запрос PUT и устанавливает заголовки Accept и Content-Type . Этот метод значитеьно упрощает наш тестовый запрос: Хорошо то, что при этом мы не теряем гибкости. Наш метод putJson(..) возвращает MockHttpServletRequestBuilder. Это позволяет нам, при необходимости, добавлять дополнительные свойства запроса в тесты (например, заголовок авторизации в этом примере). Заголовки аутентификации - еще один вопрос, с которым мы часто сталкиваемся при написании тестов Spring Mock-MVC. Однако мы не должны добавлять заголовки аутентификации к нашему предыдущему методу putJson(..). Даже если все запросы PUT требуют аутентификации, мы сохраним большую гибкость, если будем работать с аутентификацией по-другому. RequestPostProcessor может помочь нам в этом. Как следует из названия, RequestPostProcessor можно использовать для обработки запроса. Мы можем использовать его для добавления в запрос пользовательских заголовков или другой информации. Например: Метод authentication() возвращает RequestPostProcessor, который добавляет к запросу базовую аутентификацию. Мы можем применить этот RequestPostProcessor в нашем тесте с помощью метода with(..): Это не только упрощает наш тестовый запрос. Если мы изменим формат заголовка запроса, нам теперь нужно изменить только один метод, чтобы исправить тесты. Кроме того, putJson(url, data).with(authentication()) также довольно удобен для чтения. Теперь рассмотрим, как можно улучшить проверку ответов. Начнем со следующего примера: Здесь мы проверяем код состояния HTTP, убеждаемся, что для заголовка Cache-Control установлено значение no-cache, и используем выражения JSON-Path для проверки полезной содержания ответа. В Cache-Control выглядит как заголовок, который, вероятно, понадобится для проверки различных ответов. В этом случае неплохо было бы придумать небольшой метод быстрого доступа: Теперь мы можем применить проверку, передав noCacheHeader() в andExpect(..): Тот же подход можно использовать для проверки тела ответа. Например, мы можем создать небольшой метод product(..), который сравнивает ответ JSON с заданным объектом Product: Теперь наш тест выглядит так: Обратите внимание, что параметр prefix обеспечит нам гибкость. Объект, который мы хотим проверить, не всегда может находиться на корневом уровне JSON ответа. Предположим, что запрос может вернуть коллекцию продуктов. Затем мы можем использовать параметр prefix для выбора каждого продукта в коллекции. Например: С помощью методов ResultMatcher вы избегаете разброса точной структуры данных ответа по множеству тестов. Это снова поддерживает рефакторинг. Мы рассмотрели несколько способов уменьшения многословия в тестах Spring Mock-MVC. Прежде чем мы даже начнем писать тесты Mock-MVC, мы должны решить, что мы хотим протестировать и какие части приложения следует заменить имитаторами. Часто рекомендуется протестировать как можно больше с помощью стандартных модульных тестов (без Spring и Mock-MVC). Мы можем использовать настраиваемые тестовые аннотации для стандартизации нашей тестовой установки Spring Mock-MVC. С помощью небольших методов быстрого доступа и RequestPostProcessor мы можем убрать повторно используемый код запроса из методов тестирования. Обычно ResultMatcher можно использовать для улучшения проверки ответов. Вы можете найти код примеров на GitHub.