项目中为了整体代码的高内聚与可移植性, 按照maven-module的方式对代码进行了拆分, 样例如下:
- root-module - shared-common-module - middleware-module - cache-module - cache-api - cache-local-impl - cache-redis-impl - persistence-module - persistence-api - persistence-mysql - persistence-oracle - biz-module - controller-module - service-module
<modules> <module>shared-common-module</module> <module>middleware-module</module> <module>biz-module</module> </modules>
<dependencyManagement> <dependencies> </dependencies> </dependencyManagement> <dependencies> </dependencies>
依赖关系如下:
controller-module --> service-module --> cache-api --> shared-common --> cache-redis-impl/cache-local-impl --> shared-common --> persistence-api --> shared-common --> persistence-mysql --> shared-common
上述多模块导致的问题, 在SpringBoot场景下, 由于默认的Application是放在最外层的controller-module. 根据依赖关系, middleware-module, shared-common-module等无法依赖controller-module(否则会导致循环依赖) 所以由于找不到Application入口, 无法by模块地启动spring容器, 进行集成测试.
尤其是log4j.xml等配置, 由于各个模块都需要, 因此需要放入到share-common里.
例如persistence-mysql模块的配置项拆分为如下几个:
spring.datasource.driver.driver-class-name=com.mysql.jdbc.Driver spring.datasource.driver.type=com.xxx.druid.pool.DruidDataSource spring.datasource.driver.maxActive=300 spring.datasource.driver.initialSize=20
spring.datasource.jdbc-url=jdbc:mysql://xxx:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true spring.datasource.username=usr spring.datasource.password=pwd
注意: 这些 *.properties 文件一定都要放在 src/main/resources/ 目录下, 注意尤其不能把 persistence-mysql-test.properties 放到 src/test/resources/ 目录下. 因为如果放到 src/test/resources/ 下, 虽然当前模块自身的UT能加载到 persistence-mysql-test.properties 文件 但其他依赖到该模块的UT就无法加载到 persistence-mysql-test.properties 文件了.
*.properties
persistence-mysql-test.properties
@Configuration @PropertySource({"classpath:persistence-mysql-common.properties", "classpath:persistence-mysql-${spring.profiles.active}.properties"}) public class DataSourceConfig { 第三步: 配置 spring.profiles.active 环境变量 方案1: 在UT代码里显式设置环境变量 方案2: 通过springboot的默认机制设置环境变量:在对应模块的 src/test/resources/ 目录下新建 application.properties 文件, 内容如下: spring.profiles.active=test UT启动时会优先加载 src/test/resources/application.properties 文件, 这样就把环境变量设置好了. 第四步: UT@RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 三: 模块化的Spring容器测试能力由于各个模块没有Application.java类的入口, 而SpringBootTest需要有个Application入口.因此当前的解决方案是 在各个模块的src/test/java代码里, 都加入TestApplication.java文件, 作为测试的context入口: 在父包路径下创建该类, 则无需指定入口类, springboot默认会从 DefaultServiceTest测试类的包路径, 向上查找. 在兄弟路径下设置该类, 则需要指定该入口类名称如下: @RunWith(SpringRunner.class) @SpringBootTest(classes = {TestApplication.class}) @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 思考其实整体与SpringBoot的分层测试思路很像.
@Configuration @PropertySource({"classpath:persistence-mysql-common.properties", "classpath:persistence-mysql-${spring.profiles.active}.properties"}) public class DataSourceConfig {
第三步: 配置 spring.profiles.active 环境变量 方案1: 在UT代码里显式设置环境变量 方案2: 通过springboot的默认机制设置环境变量:在对应模块的 src/test/resources/ 目录下新建 application.properties 文件, 内容如下: spring.profiles.active=test UT启动时会优先加载 src/test/resources/application.properties 文件, 这样就把环境变量设置好了. 第四步: UT@RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 三: 模块化的Spring容器测试能力由于各个模块没有Application.java类的入口, 而SpringBootTest需要有个Application入口.因此当前的解决方案是 在各个模块的src/test/java代码里, 都加入TestApplication.java文件, 作为测试的context入口: 在父包路径下创建该类, 则无需指定入口类, springboot默认会从 DefaultServiceTest测试类的包路径, 向上查找. 在兄弟路径下设置该类, 则需要指定该入口类名称如下: @RunWith(SpringRunner.class) @SpringBootTest(classes = {TestApplication.class}) @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 思考其实整体与SpringBoot的分层测试思路很像.
方案1: 在UT代码里显式设置环境变量
方案2: 通过springboot的默认机制设置环境变量:在对应模块的 src/test/resources/ 目录下新建 application.properties 文件, 内容如下:
src/test/resources/
application.properties
spring.profiles.active=test
UT启动时会优先加载 src/test/resources/application.properties 文件, 这样就把环境变量设置好了.
src/test/resources/application.properties
@RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 三: 模块化的Spring容器测试能力由于各个模块没有Application.java类的入口, 而SpringBootTest需要有个Application入口.因此当前的解决方案是 在各个模块的src/test/java代码里, 都加入TestApplication.java文件, 作为测试的context入口: 在父包路径下创建该类, 则无需指定入口类, springboot默认会从 DefaultServiceTest测试类的包路径, 向上查找. 在兄弟路径下设置该类, 则需要指定该入口类名称如下: @RunWith(SpringRunner.class) @SpringBootTest(classes = {TestApplication.class}) @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 思考其实整体与SpringBoot的分层测试思路很像.
@RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds;
三: 模块化的Spring容器测试能力由于各个模块没有Application.java类的入口, 而SpringBootTest需要有个Application入口.因此当前的解决方案是 在各个模块的src/test/java代码里, 都加入TestApplication.java文件, 作为测试的context入口: 在父包路径下创建该类, 则无需指定入口类, springboot默认会从 DefaultServiceTest测试类的包路径, 向上查找. 在兄弟路径下设置该类, 则需要指定该入口类名称如下: @RunWith(SpringRunner.class) @SpringBootTest(classes = {TestApplication.class}) @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 思考其实整体与SpringBoot的分层测试思路很像.
由于各个模块没有Application.java类的入口, 而SpringBootTest需要有个Application入口.因此当前的解决方案是 在各个模块的src/test/java代码里, 都加入TestApplication.java文件, 作为测试的context入口:
src/test/java
@RunWith(SpringRunner.class) @SpringBootTest(classes = {TestApplication.class}) @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds; 思考其实整体与SpringBoot的分层测试思路很像.
@RunWith(SpringRunner.class) @SpringBootTest(classes = {TestApplication.class}) @ActiveProfiles("test") @Slf4j public class DefaultServiceTest { @Autowired DefaultService ds;
思考其实整体与SpringBoot的分层测试思路很像.
其实整体与SpringBoot的分层测试思路很像.