背景:
在Spring应用程序中,想要通过代码走查做好测试左移,单元测试是确保代码质量和功能正确性的关键。除了我们常用的TestNG 外,本次介绍一下其他常见的单元测试工具: **JUnit、Mockito、AssertJ,**来提高我们做白盒测试的效率。
一、引入依赖
在Maven项目中,我们通过添加以下依赖来引入使用
1、JUnit
JUnit是最常用的Java单元测试框架之一,它提供了丰富的API来编写和组织测试用例。
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
2、Mockito
Mockito允许我们创建模拟对象,并定义它们在测试中的行为。例如,我们可以模拟静态方法、构造函数或私有方法。可以用于模拟各个外部接口或者属性方法的数据。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
3、AssertJ
AssertJ提供了丰富的断言方法,使得断言更加简洁和易读。它支持链式断言和软断言,使得测试代码更加清晰。
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
二、常见模拟用法
1. 静态类
使用MockedStatic @Cleanup注解自动close。
@cleanup Mockedstatic<URLDecoder> urlDecoderMockedstatic =
Mockito.mockstatic(URLDecoder.class);
urlDecoderMockedstatic.when(()->URLDecoder.decode(
Mockito.any(),Mockito.anystring())).
thenReturn(JsoNutil.toJsonstr(depositTradeResp));
2. 工具类
使用mockConstruction
Mockito.mockConstruction(AesUtil.class, (util, context) -> {
Mockito.when(util.decryptMsg(Mockito.anyString())).thenReturn(true);
});
3. 私有方法
使用反射机制
Method checkCouponByQuery = createGoOrderService.getClass().getDeclaredMethod("checkCouponByQuery", List.class, List.class, List.class);
checkCouponByQuery.setAccessible(true);
try {
checkCouponByQuery.invoke(createGoOrderService, addedXPlusOrderInfoList, usableCoupon, checkCouponResultList);
} catch (Exception e) {
logger.info("异常信息:{}", e.getMessage());
}
4. 局部模拟
ReflectUtil.setFieldValue(changeOrderService, "changeInfoMapper", changeInfoMapperOne);
BDDMockito.when(changeInfoMapperOne.getSubSegOrderList()).thenReturn(Arrays.asList("sdsd", "sds"));
三、单测类的实现
下面这个基类为Spring Boot应用程序的测试提供了一个基础框架,包括测试环境的配置、测试数据的加载、以及测试过程中所需的工具和资源的管理。这有助于确保测试的一致性、可重复性和可靠性。其中loadJsonParam 方法允许测试类从类路径下的文件中加载JSON数据,这在测试需要发送JSON请求的API时很有用。init 方法在每个测试方法之前初始化 MockMvc 对象,这是测试Spring MVC应用程序的关键步骤。after 方法可以用来执行测试后的清理工作。最后,getMockMvc 和 getParamJsonStr 方法提供了对 MockMvc 对象和JSON参数字符串的访问,以便在测试中使用。
package com.csair.ecs;
import java.io.BufferedReader;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = GroupNeedServiceApplication.class)
@ActiveProfiles("test")
public class BaseTest {
public static final String UTF_8 = "UTF-8";
@Autowired
private WebApplicationContext webAppConfiguration;
private MockMvc mockMvc;
private String paramJsonStr;
public void loadJsonParam(String classPathJsonFileName) {
StringBuilder sb = new StringBuilder(300);
try (BufferedInputStream inputStream = new BufferedInputStream(
Thread.currentThread().getContextClassLoader().getResourceAsStream(classPathJsonFileName));
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, UTF_8))) {
String temp;
while ((temp = reader.readLine())!= null) {
sb.append(temp);
}
} catch (IOException e) {
e.printStackTrace();
}
this.paramJsonStr = sb.toString();
}
// 在每个测试方法执行之前,初始化MockMvc对象。
@Before
public void init() {
mockMvc = MockMvcBuilders.webAppContextSetup(webAppConfiguration).build();
}
// 在每个测试方法执行之后,可以执行清理工作。
@After
public void after() {
}
// 提供获取MockMvc对象的方法,以便在测试中使用。
public MockMvc getMockMvc() {
return mockMvc;
}
// 提供获取JSON参数字符串的方法,以便在测试中使用。
public String getParamJsonStr() {
return paramJsonStr;
}
}
四、测试类实现
下面这个测试类通过继承 BaseTest 类,利用已经配置好的测试环境和工具。每个测试方法都设计来覆盖特定的功能点,确保服务在各种输入下都能正确执行。使用 Lombok 的 @Slf4j 注解简化了日志记录的配置,使得测试代码更加简洁。
@Slf4j
public class SystemParamsConfigServiceImplTest extends BaseTest {
@Autowired
private SystemParamsConfigService systemParamsConfigService;
@Test
public void testSelectParamsPageList() {
// Test case 1
SystemParamsConfigReq req1 = new SystemParamsConfigReq();
req1.getPage().setPageNo(1);
req1.getPage().setPageSize(5);
ResponseBase<PageData<SystemParamsConfig>> responseBase1 = systemParamsConfigService.selectParamsPageList(req1);
log.info("结果:{}", JSONUtil.parseObj(responseBase1));
// Test case 2
SystemParamsConfigReq req2 = new SystemParamsConfigReq();
req2.getPage().setPageNo(1);
req2.getPage().setPageSize(5);
req2.setParamKey("dd");
ResponseBase<PageData<SystemParamsConfig>> responseBase2 = systemParamsConfigService.selectParamsPageList(req2);
log.info("结果:{}", JSONUtil.parseObj(responseBase2));
// Test case 3
SystemParamsConfigReq req3 = new SystemParamsConfigReq();
req3.getPage().setPageNo(1);
req3.getPage().setPageSize(5);
req3.setRemark("d");
ResponseBase<PageData<SystemParamsConfig>> responseBase3 = systemParamsConfigService.selectParamsPageList(req3);
log.info("结果:{}", JSONUtil.parseObj(responseBase3));
}
@Test
public void testGetByParamKey() {
ResponseBase<SystemParamsConfig> responseBase = systemParamsConfigService.getByParamKey("aa");
log.info("结果:{}", JSONUtil.parseObj(responseBase));
}
@Test
public void testInsertParamConfig() {
SystemParamsConfig paramsConfig = new SystemParamsConfig();
paramsConfig.setParamKey(RandomUtil.randomNumbers(6));
paramsConfig.setParamValue("测试一下");
paramsConfig.setOperator("wangjiansheng9");
paramsConfig.setRemark("备注一下");
// Test case 1
ResponseBase<Integer> responseBase1 = systemParamsConfigService.insertParamConfig(paramsConfig);
log.info("结果:{}", JSONUtil.parseObj(responseBase1));
setParamKey(paramsConfig.getParamKey());
// Test case 2
paramsConfig.setParamKey("ferferfrefrefrefer");
ResponseBase<Integer> responseBase2 = systemParamsConfigService.insertParamConfig(paramsConfig);
log.info("结果:{}", JSONUtil.parseObj(responseBase2));
}
}
五、IDEA查看覆盖率
在IntelliJ IDEA中,鼠标右键选中某个单元测试类或包,选择 更多运行/调试 - 使用覆盖率 - 使用覆盖率运行。
覆盖率
等待单元测试运行结束,会在IDEA右侧弹出覆盖率面板。在代码中查看具体覆盖情况。
