Spring boot多模块配置和测试隔离
我正在开发一个在maven模块中拆分的应用程序,如下所示:Spring boot多模块配置和测试隔离,spring,spring-boot,Spring,Spring Boot,我正在开发一个在maven模块中拆分的应用程序,如下所示: myApp父级 框架 模块1 坚持 服务 模块2 坚持 服务 第一部分:持久层 对于module1/persistence,源代码位于包org.company.myApp.module1.persistence中,我有一个配置类: package org.company.myApp.module1.persistence; import org.springframework.boot.autoconfigure.do
- myApp父级
- 框架
- 模块1
- 坚持
- 服务
- 模块2
- 坚持
- 服务
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 {
}
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 {
}
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 {
}
第一个问题:我在这里使用了@配置
。我没有把@SpringBootApplication
放进去,因为持久性不是一个独立的应用程序,而是一种库。然而,我应该在这里使用@SpringBootConfiguration
而不是@Configuration
看起来@SpringBootConfiguration
只是@Configuration
的别名,但尽管我读了很多文章,我还是不清楚
文件规定:
指示类提供Spring引导应用程序@配置。可以作为Spring的标准@Configuration注释的替代,以便可以自动找到配置(例如在测试中)
应用程序应该只包含一个@SpringBootConfiguration,大多数惯用的SpringBoot应用程序将从@SpringBootApplication继承它
由于带有所有模块的最终应用程序将包含多个@SpringBootConfiguration
,因此我选择了简单的@Configuration
第二部分:持久层测试
测试源在同一个包中(当然在src/test/java
下),我创建了一个特定于测试的配置类:
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 {
}
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 {
}
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 {
}
extensedmodule1persistenceconfiguration
在这里似乎没有用,因为这两个类都在同一个包中,扫描将检测并加载主配置
@springbootplication
允许我加载Spring上下文,因为我的测试使用@springbootest
检测此类
第二个问题:如果我用@SpringBootConfiguration
注释主配置,Module1PersistenceTestConfiguration
就没有用了?这里的正确方法是什么
第三部分:服务层
配置类:
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 {
}
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 {
}
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)
来触发持久层的扫描
第三个问题:尽管我对此感到满意,但也许还有更好的选择?服务层应该知道持久层,并通过向@ComponentScan
添加相应的basePackages
属性来执行完整扫描
最后一部分:服务层测试
这是我正在努力解决的问题。
我想通过模拟持久性层来测试服务层,唯一的方法是添加一个测试配置类,该类不包括主配置类,以防止spring尝试加载持久性层(由于主配置中的@Import
)失败,因为属性中没有数据源配置(为了使用H2嵌入式数据库和未绑定的嵌入式LDAP,持久性测试资源包含application.properties):
此外,我不得不为每个测试模拟来自持久性层的所有bean
最后一个问题:实现这一目标的正确方法是什么?
答案可能是:对您的服务进行单元测试,或者进行集成测试,其中包括持久性层,但您的半集成测试没有任何意义?我没有问,但在这个答案中,我假设您谈论的是maven模块,而不是java模块。如果您谈论的是Java模块,大多数仍然成立,但可能不是所有 你有很多问题,我不确定我是否能全部回答,但我可以与你分享我控制SpringBootTest上下文的策略 通常,我只在需要检查某些特定配置是否正确加载,或者是否用于测试控制器或客户端时才编写SpringBootTests。对于其他任何东西,我都编写常规的单元测试(是的,当它们是服务时)。将测试上下文保持在小范围内,并且是要测试的类的本地上下文。只将特定测试真正需要的bean加载到上下文中。如果您不控制测试上下文,那么测试上下文可能会变成维护的地狱。不要创建一个大规模的测试配置。因为在某些情况下,某些bean需要被模仿,而在其他情况下,您不想模仿它们。这可能会成为一场噩梦 通常,我的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上下文将加载所提到的类(我相信同一个包中的任何其他Bean)。如果您需要任何其他非模拟bean来获取要加载的应用程序上下文,请将它们包括在@SpringBootTest(classes={MyService.class,whateOtherBeanYouneed.class}中,但只加载您需要测试的内容
这样,应用程序中的所有springboottests都不需要一个OneTestConfigurationToRuleThemAll.class,而且每当向应用程序添加新的SpringBean时,也不会有3000个测试失败
最后两项说明:
确保SpringBootApplication类位于应用程序的根(所有模块共享的根路径)上