前言
我们的项目很多都是用maven来管理的,写代码的时候为了省事,或者赶时间,一般都不写单元测试。就算写了单元测试,打包或者发布的时候一般都会跳过单元测试。这样做的好处是:打包、发布可以成功,但是项目质量确得不到保障。
一般情况下,我们用来验证某个接口(方法)是否可以正常执行,才会编写单元测试,通常这个接口不是http接口,如果是http接口的话,就直接用postman发请求了。不得不说,新手一般都是这么干的。
写单元测试的时候,需要区分环境,或许还需要某些中间件(如:redis、mq)的支持,甚至有些时候还需要调用第三方服务,这个时候这个"单元测试"就不能算作单元测试了,而应该称为集成测试了。
阿里规范:单元测试需要遵守自动化、独立性、可重复原则。如:我们写了一个单元测试,调用别人的服务添加了一条数据,这条数据我们又不能回滚,再次执行的时候可能就会报错了,这就违背了可重复原则。这个时候,需要使用Mock模拟别人的服务,确保自己的代码正常执行。
应为mock的数据是虚假的,如果我们需要正式的调用别人的服务获取真实的接口,我们可以把这种单元测试作为集成测试单独抽离出来,打包或者发布的时候执行单元测试,跳过集成测试。
区分单元测试和集成测试。
简单的接口(interface),dao、service,操作自己的数据库,能够通过@Transactional
回滚测试过程中产生的测试数据,这种就是单元测试。
复杂的操作,如:调用别人的接口操作别人的数据库,我们不能通过@Transactional
回滚别人的数据,需要做一下补偿。好比:调用别人添加接口加了一条数据,最后的再调一下删除接口把这条数据删掉就行了。
下面教你如何分离单元测试和集成测试,同时可以随意切换spring环境。
你需要对maven或者spring boot有一定的了解。
一、通过maven指定spring环境
通常情况下,我们将某个应用进行打包,然后将jar上传到不同的环境, 并通过环境变量或者args的方式去修改application.yml
里面配置的参数。其实,我们可以直接使用maven命令将应用打包成某个环境需要的包,然后直接java -jar *.jar
,就不用再去指定环境变量或者args了。
- 指定环境,pom.xml
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<profiles.active>local</profiles.active>
<skip.unit.test>false</skip.unit.test>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<profiles.active>dev</profiles.active>
<skip.unit.test>false</skip.unit.test>
</properties>
</profile>
<profile>
<id>test</id>
<properties>
<profiles.active>test</profiles.active>
<skip.unit.test>false</skip.unit.test>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
<skip.unit.test>false</skip.unit.test>
</properties>
</profile>
</profiles>
这里配置了四个环境,本地(local)、开发(dev)、测试(test)、生产(prod)。<activeByDefault>true</activeByDefault>
表示默认使用local
- 配置文件,application.yml
在resources下面,有五个配置文件,application.yml
里面存放公共的配置,然后application-${env}.yml
表示不用环境的配置。微服务里面有一个统一的配置中心做统一配置管理,这些配置文件只需要配置只需要配置使用哪一个配置中心就可以了。更详细的配置需要写在配置中心里面。
- application.yml配置
spring:
profiles:
active: @profiles.active@
这是重点,@profiles.active@
指定的环境。这个key为上面pom.xml配置的profiles > profile > properties > profiles.active
- resources过滤与替换变量
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application*.yml</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>application.yml</include>
<include>application-${profiles.active}.yml</include>
</includes>
</resource>
</resources>
</build>
第一个resource去除了application*.yml
,第二个resource引入了application.yml和application-${profiles.active}.yml,profiles.active默认为local,可以通过环境变量指定,见下一步。
- 打包命令
# -P指定环境,下面两种形式都可。
mvn clean package -P loca
mvn clean package -Plocal
二、分离单元测试和集成测试
为什么要分离单元测试和集成测试?
单元测试,故名思议,测试的是某一个单元,最小粒度。单元测试一般都比较简单,也比较容易书写。
集成测试,通常来讲运行条件都比较苛刻,需要先启动自己的服务,可能还需要别人的服务也在线,然后才能运行集成测试。
为什么要单独提出来,不直接通过-P指定环境来区分?
我们需要单元测试和集成测试都可以在不同的环境单独或者一同执行。如果通过profiles
的来区分,就需要写2 * 4 = 8个profile
配置了,有点麻烦了,所以我们使用maven的插件,通过mvn命令和参数来作用是否执行单元测试和集成测试。
如何操作?
- 添加集成测试的目录,最终目录结构如下
这里新加了一个integration-test
目录,用来存放集成测试相关的代码。
- 引入插件,pom.xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>add-integration-test-source</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/integration-test/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-integration-test-resources</id>
<phase>generate-test-resources</phase>
<goals>
<goal>add-test-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/integration-test/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<skipTests>${skip.unit.test}</skipTests>
<excludes>
<exclude>**/*IntegrationTest.java</exclude>
<exclude>**/IT*.java</exclude>
<exclude>**/*IT.java</exclude>
<exclude>**/*ITCase.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<includes>
<include>**/*IntegrationTest.java</include>
<include>**/IT*.java</include>
<include>**/*IT.java</include>
<include>**/*ITCase.java</include>
</includes>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
<configuration>
<skipITs>false</skipITs>
</configuration>
</execution>
</executions>
</plugin>
-
build-helper-maven-plugin
这个插件的目的是,将非标准目录的源文件也添加到main或者resources,这里的配置可以理解为:src/integration-test/java >> src/test/java
,src/integration-test/resources >> src/test/resources
-
maven-surefire-plugin
打包时是否跳过单元测试和执行单元测试时排除集成测试相关的代码。 -
maven-failsafe-plugin
集成测试,需要注意集成测试类的类名。
注意:build-helper-maven-plugin和maven-surefire-plugin高版本的会出现不执行集成测试的情况,还未找到原因。
- 打包
# 默认执行单元测试,不执行集成测试
mvn clean package -P local
# 跳过单元测试
mvn clean package -P local -Dskip.unit.test=true
# 执行集成测试和单元测试
mvn clean package integration-test -P local
# 指定集成测试,跳过单元测试
mvn clean package integration-test -P local -Dskip.unit.test=true