一,背景
单元测试基本上是开发逃不过的一个工作内容,虽然往往因为过于无聊,或者过于麻烦,而停止于项目的迭代之中,不了了之了。其实不是开发们懒,而是上头要求的测试覆盖率高,但是又没有好用的工具,导致工作积压,最后只能舍弃掉这部分。
最近发现Spring+junit+mockito很好用,特别是对于DDD架构的项目,可以针对特定的代码层做单元测试,贼好用。
二,环境准备
1,首先是在spring环境下,引入mockito和junit
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
2,使用mockito
1,使用 `@ExtendWith(MockitoExtension.class)` 注解让 junit 使用 Mockito 环境启动单元测试,这样 `@Mock` 和 `InjectMocks` 注解才会生效。
2,使用 `@Mock` 注解来 mock 一个对象,方便后续控制这个对象的行为。
3,使用 `@InjectMocks` 注解来标注一个对象(注意,对象的类型是具体的实现类,而不是接口),junit 会自动将标有 `@Mock` 注解的对象按需注入其中。
@ExtendWith(MockitoExtension.class)
class JunitPracticeTest {
@Mock
private JunitGateway junitGateway;
@Mock
private JunitMapper junitMapper;
@InjectMocks
private JunitServiceImpl junitService;
@BeforeAll
static void setup() {
MockStaticUtil.mockStatic(BeanUtil.class);
}
@Test
void testAdd() {
when(BeanUtil.getBean(JunitGateway.class)).thenReturn(junitGateway);
// ...
}
}
3,覆盖率检测
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<!-- 这里配置的include和exclude是对整个聚合报告生效的 -->
<configuration>
<includes>
<!-- 仅统计app层和domain层的特定类 -->
<include>com/spice/junit/service/**/*</include>
<include>com/spice/junit/**/*DO.class</include>
</includes>
<excludes>
<!-- 排除这个特殊类,它是infrastructure层的一个类 -->
<exclude>com/spice/junit/service/RedisService.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>pre-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>post-test</id>
<phase>test</phase>
<goals>
<!-- 在app模块中聚合jacoco的报告 -->
<goal>report-aggregate</goal>
</goals>
<configuration>
<!-- 配置在聚合报告中包含本模块(risk-app模块) -->
<includeCurrentProject>true</includeCurrentProject>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
三,实战
1,新建测试类
idea中在类里ctrl+shift+T
注意:只有public方法才能生成单元测试方法,也只有public方法才需要单元测试。
2,添加注解和注入业务类里依赖的Bean
原业务类:
单元测试类:
将原业务类依赖的mapper注入到单元测试类。 将自己的实现类也注入到单元测试类。 下面的例子中还把BeanUtil这个静态的工具类先mock了,因为后面会用到,各种工具类都可以这样来mock。
3,根据原业务类方法里的逻辑写单元测试用例
4,redis等中间件都可以mock
5,捕获自定义异常
四,补充
import java.util.HashSet;
import java.util.Set;
import org.mockito.Mockito;
public class MockStaticUtil {
private static final ThreadLocal<Set<Class<?>>> EXECUTED = ThreadLocal.withInitial(HashSet::new);
public static void mockStatic(Class<?> classToMock) {
Set<Class<?>> executed = (Set)EXECUTED.get();
if (!executed.contains(classToMock)) {
Mockito.mockStatic(classToMock);
executed.add(classToMock);
EXECUTED.set(executed);
}
}
private MockStaticUtil() {
}
}