
Всем привет! Я Юнес, 
Задача: протестировать функцию в сервисе достижений «рассказать о достижении в соцсетях». Фича не связана с данными пользователя и использует только интеграцию “Social Service”. Запускаем все тесты с тегом “share-feature” и… получаем org.postgresql.util.PSQLException!
Причина ошибки не так важна. Это может быть и опечатка в адресе базы, и заблокированный пользователь, и проблемы с доступом. Главное, что мы понимаем: несмотря на то, что запускаемые тесты не имеют никакого отношения к базе с пользователями, Spring инициализирует бин DataSource, который устанавливает соединение с базой.
Лишние бины в контексте приводят не только к повышенному потреблению ресурсов и увеличению времени выполнения, но и к непредсказуемому поведению, проблемам с отладкой и нестабильности. Неприятностей можно избежать, если оптимизировать Spring-контекст, желательно — сохранив нервы и позитивный настрой в процессе.
Контекст в Spring — это основной интерфейс, который управляет бинами, внедряет зависимости (пресловутый Dependency Injection), обеспечивает доступ к ресурсам и делает много других полезных вещей.
Оптимизация ради оптимизации — не лучшая идея. Вот пример признаков, которые могут быть симптомами проблем с неоптимальным контекстом:
-
Медленное выполнение тестов. Если с ростом количества тестов время выполнения растет непропорционально, то одним из источников проблемы может быть неоптимальная конфигурация или чрезмерное использование бинов.
-
Высокое потребление памяти. Если в коде нет рекурсий или утечек памяти, а тесты все равно требуют больше и больше памяти, возможно, дело в контексте.
-
Нестабильные тесты. Если тесты периодически падают с исключениями вроде BeanCreationException, NoSuchBeanDefinitionException, UnsatisfiedDependencyException и другими, указывающими на проблемы со Spring, нужно приступать к оптимизации.
-
Жесткие требования к тестовому окружению. Если вы даже один тест не можете запустить без всех ресурсов, доступов, баз данных, иначе он падает с “Failed to load ApplicationContext”, это может быть признаком проблем с контекстом.
Конфигурация в управлении контекстом
Классы конфигурации в Spring сигнализируют о том, что являются источником бинов. В конфигурациях можно задать @Configuration @ComponentScan(basePackages = "com.example") // Предполагается, что 'com.example' это корневой пакет проекта public class AchievementConfig{ … } Стоит выбирать специфичный пакет для сканирования: @Configuration @ComponentScan(basePackages = "com.example.achievement.service") public class AchievementConfig{ … }
@Import — для регистрации конкретного класса конфигурации в Spring-контексте. Один из частых вариантов использования — импорт нужного класса конфигурации. @EnabelAutoConfiguration — включает автоконфигурацию Spring контекста. Те, кто работает со Spring Boot, знакомы с понятием автоконфигурации, когда Spring на основе зависимостей и пропертей регистрирует бины, которые могут вам пригодиться. Автоконфигурация автоматически отключается при использовании сегментации. Поскольку Spring не всегда успешно угадывает, какие бины нам нужны, а также тратит время на сканирование зависимостей, лучше не использовать @EnabelAutoConfiguration для тестов. Может сложиться ситуация, что вы хотите использовать автоконфигурацию, но вам нужно отключить часть бинов. Тогда вы можете либо использовать @EnabelAutoConfiguration со списком отключенных конфигураций: Либо настроить application.properties/application.yml: Контекст для атвотестов поднимался за 120 секунд. Если что-то шло не так, приходилось вносить правки в код и перезапускать тесты, что делало отладку тестов настоящим кошмаром… Проблема заключалась в неоптимальной конфигурации автотестов. Каждый тест загружал весь Spring Boot контекст приложения, что включало множество ненужных бинов и конфигураций. Это избыточное сканирование и инициализация значительно увеличивали время выполнения тестов. Заменили Создали специализированный конфигурационный класс Использовали Код до изменений Код после изменений После внесения изменений, среднее время выполнения одного теста сократилось до 30 секунд, что в 4 раза быстрее по сравнению с предыдущим временем выполнения. Это значительно ускорило процесс отладки. Конфигурация в Spring — ключевой инструмент для определения, какие бины будут зарегистрированы и будут контролироваться на протяжении всего времени выполнения кода. При оптимизации важно, какие бины конфигурация задействует и нет ли лишних, как настроено сканирование и есть ли автоконфигурация. Когда мы говорим про оптимизацию конфигурации, здесь минимум усилий может дать отличный результат, если ранее не проводился рефакторинг. Сегментация, или test slicing, — это механизм Spring Boot, который позволяет разделить контекст на слои, чтобы тестировать их в изоляции друг от друга. В тестовом классе нужно указать аннотацию сегмента, и Spring инициализирует ограниченный контекст, который включает бины, относящиеся к данному сегменту. Доступны аннотации: @SpringBootTest — позволяет тестировать все сегменты приложения совместно, и нет исключений по загружаемым бинам. @WebMvcTest — для тестов слоя Model-View-Controller. Включает бины, связанные с Controller. @WebFluxTest — включает бины с Controller, но только для reactive-контекста. @RestClientTest — для тестирования Rest-взаимодействия. Автоматически создает для тестов мок rest-сервиса. @WebServiceClientTest, @WebServiceServerTest — для тестирования SOAP клиента или сервера. @JsonTest — тесты, завязанные только на Json-сериализацию. Например, JsonComponent. @DataJpaTest — для тестов, которые используют только JPA-компоненты. Задействует транзакции в тестах, откатывает действия после прогона каждого теста и задействует готовую среду, в которой используется база данных in-memory. @JdbcTest — аналогично @DataJpaTest позволяет использовать транзакции, только для JDBC-компонент. @JooqTest, @DataElasticsearchTest, @DataRedisTest, @DataNeo4jTest, @DataCassandraTest, @DataCouchbaseTest, @DataMongoTest, @DataR2dbcTest — для тестов, которые нацелены на работу с конкретной базой или системой управления БД. @GraphQlTest — для тестов, которые выполняют GraphQL-запросы. @DataLdapTest — для тестов, связанных с LDAP-протоколом. Можно создать свою аннотацию, чтобы выделить нужный тип бинов, которые будут обязательно присутствовать в контексте вашего теста. Автотесты для сервиса достижений выполнялись долго, и тесты не были изолированы друг от друга, возникали ошибки связанные с БД, даже когда запускались тесты контроллера. Из-за этого время выполнения тестов увеличивалось, а также возникали проблемы с повторяемостью результатов тестирования. Тесты использовали общую конфигурацию, загружали весь контекст Spring Boot приложения, что приводило к длительному времени инициализации и отсутствию изоляции между тестами. Использовали Использовали Использовали специализированные конфигурации для различных типов тестов, чтобы обеспечить их изоляцию и ускорить выполнение. Код до изменений Код после изменений Web Layer Test Data Layer Test После внесения изменений, проблем с падением тестов стало значительно меньше благодаря изоляции. Количество flaky тестов уменьшилось на 20%. Скорость поднятия контеста также уменьшилась из-за того, что меньше бинов стало инициализироваться, но незначительно из-за того, что запускаемых контекстов стало больше. Явными преимуществами сегментации являются простота и удобство использования. Набор бинов уже предопределен, автоконфигурация сделает все за вас. Слои изолируются от остального контекста, что повышает контролируемость тестирования. Но аннотации недостаточно гибкие, если нужен детальный контроль над загружаемыми бинами. Не все тесты нуждаются в Spring-контексте. Можно оставить часть тестов без него, например юнит-тесты. Spring предоставляет набор аннотаций для указания условий инициализации бина. Если условие выполняется, бин может быть зарегистрирован в контексте. Вот список условий, которые наиболее применимы для тестовых конфигураций и других бинов: @ConditionalOnBean — если указанный бин есть в контексте. @ConditionalOnClass — если указанный класс есть в classpath. @ConditionalOnMissingBean — если бина нет в контексте. @ConditionalOnMissingClass — если класса нет в classpath. @ConditionalOnProperty — проверяет значение проперти в application.yml или application.properties. @ConditionalOnResource — проверяет наличие файла в classpath. @ConditionalOnExpression — если логическое выражение возвращает true, бин инициализируется. Для составления выражений используется Spring Expression Language. @ConditionalOnJava — бин инициализируется, если версия Java входит в перечень разрешенных. Для одного бина можно использовать несколько условий. Я перечислил не все аннотации-условия, а наиболее подходящие для использования с тестами. Список всех актуальных аннотаций можно посмотреть на GitHub. Создадим свою аннотацию-условие: Реализуем интерфейс Condition: У нас возникла проблема с длительным временем загрузки контекста Spring в тестах сервиса достижений. Есть несколько тяжеловесных бинов, которые нужны в небольшом количестве тестов, но они инициализируются всегда, что занимает +10-15 секунд к времени инициализации контекста. Мы выделили несколько сценариев, когда мы точно используем эти бины. Изучив все варианты оптимизации, мы обнаружили, что использование условий удобнее всего, т.к. нам нужно точечное изменение, создание новой конфигурации в данном случае не оправдано. Создание кастомного условия: Было создано кастомное условие Аннотация бинов: Мы использовали аннотацию Настройка свойств: В тестах, где нужна указанная зависимость, было добавлено свойство Код до изменений Код после изменений Кастомное условие Конфигурация Тест После оптимизации тесты без тяжелых бинов стали запускаться быстрее, плюс мы добились лучшей изоляции тестов. Вариантов применения в тестах аннотаций-условий — масса. Можно использовать их для инициализации конфигурации, если @TestConfiguration public class TestConfig { @Bean @Primary public HeavyService heavyServiceMock() { return Mockito.mock(HeavyService.class); } }
В Spring есть возможность привязать бины к различным окружениям. Например, мы можем создать два класса для настройки соединений с базой данных. Для тестирования используем базу H2, а для прода — Postgresql. Можно привязывать бины к нужному нам окружению и адаптировать к разным условиям. @Profile — аннотация указывает на профили, в которых бин должен или не должен быть инициализирован. @ActiveProfile — аннотация для тестов, которая говорит Spring активировать указанные профили при запуске теста. Это нужно для теста бинов, которые инициализируются только при определенном профиле. Можно тестировать, одновременно задействуя несколько профилей. Профиль нужен для определения поведения в зависимости от окружения. Перед созданием нового профиля определите список окружений, в которых будут запущены тесты. Настройте классы конфигурации под каждое из них, используя аннотацию Profile для указания окружения. Так разграничится, какие бины нужны в текущем окружении, а какие можно не инициализировать и не помещать в контекст. Это позволяет экономить время и ресурсы. Профилями не стоит злоупотреблять — например, создавать профили под каждую базу: dev-db-h2, dev-db-mysql… Дело в том, что при запуске теста с новым профилем Spring создает новый контекст. Поэтому злоупотребление аннотациями ActiveProfile может замедлить выполнение тестов. Отсутствие разбивки бинов по окружениям привело к необходимости иметь доступ к базам данных на qa и dev контурах одновременно для стендов. Это усложняет управление конфигурациями и увеличивает риск ошибок. Также это создаёт проблему с безопасностью. Все бины и конфигурации загружаются независимо от окружения, что приводит к избыточным подключениям и конфигурациям, которые не нужны для конкретного окружения. Создание профилей для разных окружений (dev и qa): Было определно два Spring профиля (qa и dev) для управления конфигурацией бинов в зависимости от окружения. Определение специфичных конфигураций для каждого окружения: Разделение конфигурационных файлов и бинов для dev и qa окружений. Код до изменений application.yml Тест Код после изменений application-dev.yml application-qa.yml Тест Данные изменения избавили команду от многих проблем. Лучшая изоляция позволяет не обращать внимание на доступность БД из другого контура. Бины, которые нужны в qa окружении не инициализируются в dev окружении и наоборот, что экономит немного ресурсов и времени. Главная задача профилей — обеспечить гибкую настройку контекста в зависимости от окружения. А еще они положительно влияют на изолированность тестов и производительность, поскольку в тестовой среде инициализируются только бины и конфигурации, нужные нам для тестирования. Мы стараемся избегать операций создания или пересоздания контекста, потому что Spring-контекст выполняет большое количество различных операций при запуске. Он проходит пять этапов инициализации, во время которых парсит конфигурацию, сканирует пакеты, регистрирует и создает бины, настраивает их. Все это требует ресурсов и времени. И чем больше бинов используется, тем дольше это происходит. Вот список аннотаций, которые влияют на работу контекста, но не подошли под остальные категории. @Lazy — аннотация информирует Spring о том, что для данного бина нужна ленивая инициализация, то есть Spring инициализирует его только тогда, когда он будет вызван в коде. В тестах это помогает ускорить инициализацию контекста и снизить количество потребляемых ресурсов. Ею стоит помечать тяжелые бины, а также редко используемые компоненты. В отличие от бинов с другим профилем или бинов с невыполненным условием инициализации, ленивый бин присутствует в контексте и не вызывает UnsatisfiedDependencyException при попытке обращения к нему. @DirtiesContext — информирует Spring о том, что контекст требуется перезапустить до или после выполнения указанного метода или класса. Она используется в тестах, чтобы возвращать исходное состояние бинам, очищать кэш, пересоздавать соединение с базой данных и так далее. Проблема в том, что пересоздание контекста — ресурсоемкая операция, которая @Service public class AchievementService { @Cacheable(value = "achievements", key = "#achievementId") public Achievement getAchievementById(Long achievementId) { // код метода } @CachePut(value = "achievements", key = "#achievement.id") public Achievement updateAchievement(Achievement achievement) { // код метода return achievement; } @CacheEvict(value = "achievements", key = "#achievementId") public void deleteAchievement(Long achievementId) { // код метода } }
Стало так: В Spring кэшируются не только повтояемые и ресурсоёмких операции, но и контексты. Если для первого нужно явно указывать аннотации вроде Cachable, то с контекстами немного сложнее. Как только Spring загружает ApplicationContext (или WebApplicationContext) для теста, этот контекст кэшируется и повторно используется для всех последующих тестов, которые объявляют ту же уникальную конфигурацию контекста в том же наборе тестов. Как упоминалось ранее, аннотации вроде MockBean и SpyBean меняют конфигурацию контекста, что не позволяет её переиспользовать. Также любое изменение метаданных конфигурации (активные профили, подключенные классы конфигурации, родительский контекст и пр.) создаёт новый контекст, а не переиспользует старый. Поэтому, где это возможно, стоит не использовать аннотации, создающие уникальные контексты и искать баланс между изоляцией и модульностью и производительностью. За время работы со Spring я вывел для себя два момента, как не сломать механизм кэширования и обернуть его в свою пользу: Запускать тесты, которые используют одинаковый контекст, в одном процессе. Контекст хранится в статической переменной в JVM, поэтому кэширование фактически не будет работать при запуске тестов в разных процессах. В Spring сводят кэширование к нулю Fork Mode в Maven или maxParallelForks в Gradle, которые запускают несколько процессов параллельно. Кэширование требует ресурсов, не стоит использовать его для всего подряд, это сделает только хуже. Не кэширую данные в тестах, если они часто меняются, зависят от контекста, узкоспециализированные или часто меняются. А еще слишком дорого кэшировать примитивы и легковычисляемые данные. Кэширование полезно тогда, когда испробовано большинство решений по оптимизации, но нужно выжать еще из кода. При неправильном использовании кэширования могут начать падать тесты, потому что данные, получаемые из кэша, устарели или некорректны. Перед оптимизацией контекста стоит понять его текущее состояние. Это будет точка, от которой будем отталкиваться. Нам важны следующие метрики. Время инициализации контекста Что означает: сколько времени нужно Spring, чтобы выполнить полную инициализацию контекста, начиная от парсинга конфигураций и заканчивая настройкой созданных бинов. Зачем нужна: сведение к минимуму времени запуска ускоряет выполнение тестов, способствуя ускорению циклов разработки и обратной связи. Чем измерять: — Отлавливать события старта и завершения инициализации Spring-контекста с помощью ApplicationListener и считать разницу. Потребление памяти Что означает: количество оперативной памяти, которое используется во время выполнения кода. Зачем нужна: для предотвращения проблем с памятью при запуске в окружении с ограниченными ресурсами. Чем измерять: профилировщик — VisualVM, JProfiler и так далее. Сканируемые бины для тестов Что означает: какие бины Spring подтягивает для тестов. Зачем нужна: для правильной конфигурации ComponentScan — чтобы не загружать ненужные бины. Чем измерять: Spring выводит информацию о работе ComponenScan в логи, нужно выставить уровень логирования DEBUG и искать в логах информацию о ClassPathBeanDefinitionScanner через Ctrl+F. В DEBUG-логах можно найти информацию о работе автоконфигурации, она выводится в CONDITIONS EVALUATION REPORT. Помимо этого, можно понаблюдать за работой кэша Spring (если вы кэшируете данные), включив логирование в application.properties: logging.level.org.springframework.test.context.cache=DEBUG. После сбора метрик можно приступать к рефакторингу. После рефакторинга нужно повторно собрать метрики и сравнить. Также стоит повторно запустить тесты и убедиться, что изменения не повлияли на результаты — по крайней мере, в худшую сторону. Оптимизация контекста Spring для тестов заключается не только в повышении скорости выполнения тестов или экономии ресурсов. Речь идет также об обеспечении надежности, удобстве обслуживания и масштабируемости процесса тестирования, а также увеличении изолированности тестов. Надеюсь, приведенные в статье рекомендации помогут кому-нибудь улучшить тесты и сделать процесс тестирования эффективнее и стабильнее. Если вы знаете способы оптимизации контекста, не приведенные в статье, — буду рад, если поделитесь в комментариях 🙂@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})spring.autoconfigure.exclude= \ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, \ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, \ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurationПример
Проблема
Диагностика
Внесенные изменения
@SpringBootTest на @ContextConfiguration: Это позволило загружать только необходимую конфигурацию для тестов.TestConfig, который включал только те компоненты, которые действительно нужны для тестирования сервиса достижений.@ComponentScan с указанием конкретного пакета: Это ограничило сканирование только необходимым пакетом, содержащим AchievementService.@RunWith(SpringRunner.class) @SpringBootTest public class AchievementServiceTest { @Autowired private AchievementService achievementService; @Test public void testAchievement() { // Тестирование логики выдачи достижений } }@RunWith(SpringRunner.class) @ContextConfiguration(classes = {TestConfig.class}) public class AchievementServiceTest { @Autowired private AchievementService achievementService; @Test public void testAchievement() { // Тестирование логики выдачи достижений } } @Configuration @ComponentScan(basePackageClasses = AchievementService.class) public class TestConfig { // Конфигурация тестов, включающая только необходимые компоненты }Результат
Сегментация
Пример
Проблема
Диагностика
Внесенные изменения
@WebMvcTest: Это позволило тестировать только веб-слой, не загружая весь контекст приложения.@DataJpaTest: Это позволило тестировать только слой данных, не загружая другие части приложения.@RunWith(SpringRunner.class) @SpringBootTest public class AchievementServiceIntegrationTest { @Autowired private MockMvc mockMvc; @Test public void testGetAchievement() throws Exception { mockMvc.perform(get("/achievements/1")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.id").value(1)); } @Autowired private AchievementRepository achievementRepository; @Test public void testFindAchievementById() { Achievement achievement = achievementRepository.findById(1L).orElse(null); assertNotNull(achievement); assertEquals(1L, achievement.getId().longValue()); } } @RunWith(SpringRunner.class) @WebMvcTest(AchievementController.class) public class AchievementControllerTest { @Autowired private MockMvc mockMvc; @Test public void testGetAchievement() throws Exception { mockMvc.perform(get("/achievements/1")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.id").value(1)); } } @RunWith(SpringRunner.class) @DataJpaTest public class AchievementRepositoryTest { @Autowired private AchievementRepository achievementRepository; @Test public void testFindAchievementById() { Achievement achievement = achievementRepository.findById(1L).orElse(null); assertNotNull(achievement); assertEquals(1L, achievement.getId().longValue()); } } Результат
Условия инициализации бина
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Conditional(OnDatabaseTypeCondition.class) public @interface ConditionalOnDatabaseType { DatabaseType value(); }public class OnDatabaseTypeCondition implements Condition { @Override public boolean matches(@NotNull ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnDatabaseType.class.getName()); if (attributes != null) { DatabaseType requiredDatabaseType = (DatabaseType) attributes.get("value"); String configuredDriverClassName = context.getEnvironment().getProperty("spring.datasource.driverClassName"); DatabaseType configuredDatabaseType = DatabaseType.fromDriverClassName(configuredDriverClassName); return requiredDatabaseType.equals(configuredDatabaseType); } else { return false; } } }Пример
Проблема
Диагностика
Внесенные изменения
OnTestPropertyCondition, которое проверяет наличие определённого свойства в конфигурации.@Conditional для бинов, которые не всегда нужны, и настроили их загрузку в зависимости от свойства test.condition.enabled.test.condition.enabled=true, чтобы активировать необходимые бины только для нужных тестов.@Configuration public class AchievementServiceTestConfig { @Bean public AchievementService achievementService() { return new AchievementService(); } // Другие бины }public class OnTestPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String property = context.getEnvironment().getProperty("test.condition.enabled"); return Boolean.parseBoolean(property); } }@Configuration public class AchievementServiceTestConfig { @Bean @Conditional(OnTestPropertyCondition.class) public AchievementService achievementService() { return new AchievementService(); } // Другие бины }@SpringBootTest @TestPropertySource(properties = {"test.condition.enabled=true"}) public class AchievementServiceTest { @Autowired private AchievementService achievementService; @Test public void testAchievementService() { assertNotNull(achievementService); // дополнительные проверки } }Результат
Профили
Пример
Проблема
Диагностика
Внесенные изменения
@Configuration public class DatabaseConfig { @Autowired private DevDatabaseProperties devDatabaseProperties; @Autowired private QaDatabaseProperties qaDatabaseProperties; @Bean public DataSource qaDataSource() { return DataSourceBuilder.create() .url(qaDatabaseProperties.getUrl()) .username(qaDatabaseProperties.getUsername()) .password(qaDatabaseProperties.getPassword()) .driverClassName(qaDatabaseProperties.getDriverClassName()) .build(); } @Bean public DataSource devDataSource() { return DataSourceBuilder.create() .url(devDatabaseProperties.getUrl()) .username(devDatabaseProperties.getUsername()) .password(devDatabaseProperties.getPassword()) .driverClassName(devDatabaseProperties.getDriverClassName()) .build(); } }dev: datasource: url: jdbc:h2:mem:devdb driver-class-name: org.h2.Driver username: ${DB_USERNAME} password: ${DB_PASSWORD} qa: datasource: url: jdbc:h2:mem:qadb driver-class-name: org.h2.Driver username: ${DB_USERNAME} password: ${DB_PASSWORD}@RunWith(SpringRunner.class) @SpringBootTest public class AchievementServiceDevTest { @Autowired private AchievementDevService achievementService; @Test public void testAchievement() { // Тестирование логики выдачи достижений на dev окружении } } @RunWith(SpringRunner.class) @SpringBootTest public class AchievementServiceQaTest { @Autowired private AchievementQaService achievementService; @Test public void testAchievement() { // Тестирование логики выдачи достижений на dev окружении } }@Configuration @Profile("dev") public class DevDatabaseConfig { @Autowired private DatabaseProperties properties; @Bean public DataSource dataSource() { return DataSourceBuilder.create() .url(properties.getUrl()) .username(properties.getUsername()) .password(properties.getPassword()) .driverClassName(properties.getDriverClassName()) .build(); } }spring: datasource: url: jdbc:h2:mem:devdb driver-class-name: org.h2.Driver username: ${DB_USERNAME} password: ${DB_PASSWORD}spring: datasource: url: jdbc:h2:mem:qadb driver-class-name: org.h2.Driver username: ${DB_USERNAME} password: ${DB_PASSWORD}@RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("dev") public class AchievementServiceDevTest { @Autowired private AchievementService achievementService; @Test public void testAchievementDev() { // Тестирование логики выдачи достижений на dev окружении } } @RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("qa") public class AchievementServiceQaTest { @Autowired private AchievementService achievementService; @Test public void testAchievementQa() { // Тестирование логики выдачи достижений на qa окружении } } Результат
Дополнительная настройка контекста
@Service @CacheConfig(cacheNames = "achievements") public class AchievementService { @Cacheable(key = "#achievementId") public Achievement getAchievementById(Long achievementId) { // код метода } @CachePut(key = "#achievement.id") public Achievement updateAchievement(Achievement achievement) { // код метода return achievement; } @CacheEvict(key = "#achievementId") public void deleteAchievement(Long achievementId) { // код метода } } @Caching — аннотация, которая позволяет указать несколько действий с кэшем для одного метода: @Caching( evict = { @CacheEvict("achievements"), @CacheEvict(value = "achievementIds", allEntries = true) }, put = { @CachePut(value = "achievements", key = "#achievement.id") } ) public Achievement saveAchievement(Achievement achievement) { /* код метода */ }
Анализируем результат
— Брать замеры от Spring. Spring при старте замеряет время старта контекста и выводит в виде сообщения “Started ExampleTest in 4.822 seconds (JVM running for 5.224)”. Нужно только установить уровень логирования не ниже INFO.Выводы
ссылка на оригинал статьи https://habr.com/ru/articles/816051/
Добавить комментарий