Spring-boot многомодульная конфигурация и тестовая изоляция - PullRequest
0 голосов
/ 03 марта 2020

Я работаю над приложением, разделенным на модули maven следующим образом:

  • myApp-parent
    • framework
    • module1
      • сохраняемость
      • сервис
    • module2
      • постоянство
      • сервис

Первая часть: слой постоянства

Для модуля 1 / постоянство источники расположены в пакете org.company.myApp.module1.persistence, и у меня есть следующий класс конфигурации:

package org.company.myApp.module1.persistence;

import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.ldap.repository.config.EnableLdapRepositories;

import org.company.myApp.framework.FrameworkConfiguration;

@Configuration
@Import(FrameworkConfiguration.class)
@EntityScan
@ComponentScan
@EnableLdapRepositories("org.company.myApp.module1.persistence.ldap")
@EnableJpaRepositories("org.company.myApp.module1.persistence.db")
public class Module1PersistenceConfiguration {

}

Первый вопрос : я использовал @Configuration здесь. Я не поставил @SpringBootApplication, потому что постоянство - это не отдельное приложение, а всего лишь библиотека. Тем не менее, я должен использовать @SpringBootConfiguration здесь вместо @Configuration?

Похоже, @SpringBootConfiguration это просто псевдоним @Configuration, но, несмотря на многие статьи, которые я прочитал, мне все еще неясно.

В документации указано:

Указывает, что класс предоставляет Spring Boot application @Configuration. Может использоваться в качестве альтернативы стандартной аннотации Spring @Configuration, так что конфигурация может быть найдена автоматически (например, в тестах).

Приложение должно включать только одно @SpringBootConfiguration и большинство idiomati c Spring Boot приложений. будет наследовать его от @ SpringBootApplication.

Поскольку конечное приложение со всеми модулями будет содержать несколько @SpringBootConfiguration, я выбрал простую @Configuration.

Вторая часть : тесты персистентного уровня

Источники тестов находятся в одном пакете (конечно, под src/test/java), и я сделал тест, специфицирующий c класс конфигурации:

package org.company.myApp.module1.persistence;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.ldap.LdapProperties;
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.support.LdapContextSource;

@SpringBootApplication
public class Module1PersistenceTestConfiguration extends Module1PersistenceConfiguration {

}

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * extends Module1PersistenceConfiguration * * * * * * * * * *1067* Здесь кажется бесполезным, поскольку оба класса находятся в одном пакете, сканирование обнаружит и загрузит основную конфигурацию.

@SpringBootApplication позволяет мне загружать контекст Spring в качестве моих тестов). с помощью @SpringBootTest определить этот класс.

Второй вопрос: Если я аннотировал основную конфигурацию с помощью @SpringBootConfiguration, т он Module1PersistenceTestConfiguration был бы бесполезен? Какой здесь правильный подход?

Третья часть: уровень обслуживания

Класс конфигурации:

package org.company.myApp.module1.service;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import org.company.myApp.module1.persistence.Module1PersistenceConfiguration;

@Configuration
@ComponentScan
@Import(Module1PersistenceConfiguration.class)
public class Module1ServiceConfiguration {

}

Как уровень обслуживания и Слой персистентности не имеет того же пакета, я использую @Import(Module1PersistenceConfiguration.class) для запуска сканирования слоя персистентности.

Третий вопрос: Хотя я чувствую себя комфортно, возможно, есть лучшая альтернатива? Сервисный уровень должен знать о постоянном уровне и выполнить полное сканирование, добавив соответствующее свойство basePackages в @ComponentScan?

Заключительная часть: тесты сервисного уровня

Это та часть, с которой я борюсь. Я хотел бы протестировать сервисный уровень, посмеявшись над уровнем персистентности, и единственный способ сделать это - добавить класс тестовой конфигурации, который исключает основной класс конфигурации, чтобы предотвратить попытки Spring загрузить загрузочный слой (из-за @Import в основной конфигурации) и происходит сбой, так как в свойствах нет конфигурации источника данных (ресурсы персистентных тестов содержат application.properties для использования встроенной базы данных H2 и встроенного LDAP без привязки):

package org.company.myApp.module1.service;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;

@SpringBootApplication
@ComponentScan(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Module1ServiceConfiguration.class))
public class Module1ServiceTestConfiguration {

}

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

Последний вопрос: Каков будет правильный путь для достижения этого? Ответ может быть следующим: проводить модульное тестирование в вашей службе или интеграционное тестирование, которое включает в себя постоянный уровень, но ваши полуинтеграционные тесты не имеют никакого смысла?

1 Ответ

0 голосов
/ 03 марта 2020

Я не спрашивал, но в этом ответе я предположил, что вы говорите о модулях maven, а не о java модулях. Если вы говорите о Java модулях, то большинство из них верны, но, возможно, не все.

У вас много вопросов, и я не уверен, что смогу ответить на них все, но я могу поделиться с Вы моя стратегия для управления контекстом моего SpringBootTest.

Обычно я пишу SpringBootTests только тогда, когда есть какая-то конкретная c конфигурация, которую я хочу проверить, загружена правильно или для тестирования контроллеров или клиентов. Для чего-то еще я пишу регулярные модульные тесты (да и когда они являются сервисами). Держите тестовый контекст маленьким и локальным для класса, который вы хотите протестировать. Загружайте в контекст только те компоненты, которые вам действительно нужны для этого конкретного теста. Тестовые контексты могут стать адом для поддержания, если вы не держите их под контролем. Не создавайте одну массивную TestConfiguration. Потому что в некоторых случаях некоторые бобы нужно дразнить, а в других случаях вы не хотите их дразнить. Это может стать абсолютным кошмаром

Обычно мои SpringBootTests настраиваются следующим образом:

@SpringBootTest(classes = MyService.class)
@Import(MyServiceTestConfiguration.class)
class MyServiceTest {
   // test logic
}

@TestConfiguration
class MyServiceTestConfiguration.class {

    @MockBean
    BeanINeedMocked beanINeedMocked;
    // or whatever other strategy you want to use to create a mocked bean
}

Определяя классы в @SpringBootTest, он не будет искать приложение @SpringBootApplication в пути к классам. Когда тест загружен, контекст Spring загрузит упомянутый класс (и я полагаю, что любой другой Бин в том же пакете). Если для загрузки контекста приложения требуются другие немодальные компоненты, включите их в @SpringBootTest (classes = {MyService.class, WhatEverOtherBeanYouNeed.class} Но загружайте только то, что вам нужно, чтобы протестировать все, что вы хотите протестировать!

Таким образом, вам не нужен OneTestConfigurationToRuleThemAll.class для всех ваших Springboottest в вашем приложении, и у вас не будет 3000 неудачных тестов при добавлении нового Spring-бина в ваше приложение.

Два заключительных замечания: Убедитесь, что ваш класс SpringBootApplication находится в root вашего приложения (путь root, общий для всех модулей). Таким образом, вам никогда не придется использовать @ComponentScan или что-то подобное. Пока все @Configuration, @Service, @Component имеют полный путь, в котором находится приложение SpringBootApplication, все будут сканироваться и загружаться автоматически.

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

Кроме того, если вы используете @SpringBootApplication, я не буду нигде использовать @SpringBootConfiguration, а просто придерживаться @ Конфигурация.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...