谈一谈单元测试
写在前面
一 我们的现状
开发成本
维护成本
ROI
二 怎么解决
测试数据 (被测数据,和依赖对象) 测试方法 返回值断言
publicvoid testAddGroup() {
// 数据
BuyerGroupDTO groupDTO = new BuyerGroupDTO();
groupDTO.setGmtCreate(newDate());
groupDTO.setGmtModified(newDate());
groupDTO.setName("中国");
groupDTO.setCustomerId(customerId);
// 方法
Result<Long> result = customerBuyerDomainService.addBuyerGroup(groupDTO);
// 返回值断言
Assert.assertTrue(result.isSuccess());
Assert.assertNotNull(result.getData());
}
1 测试框架选择
"racecar", "radar", "able was I ere I saw elba" }) (strings = {
void palindromes(String candidate) {
assertTrue(StringUtils.isPalindrome(candidate));
}
public void test2Test(JSONObject arg) {
Animal animal = JSONObject.parseObject(arg.getString("Animal"),Animal.class);
List<String> stringList = JSONObject.parseArray(arg.getString("List<String>"),String.class);
when(testService.testOther(any(Student.class))).thenReturn(stringArg);
when(testService.testMuti(any(List.class),any(Integer.class))).thenReturn(stringList);
when(testService.getAnimal(any(Integer.class))).thenReturn(animal);
String result = kMPAlgorithm.test2();
//todo verify the result
}
2 mock框架
3 数据库层
三 Junit5和Mockito
1 使用方法
分别单独引入依赖,推荐引入最新版
<!-- junit5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<!-- mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
<!-- mockito 的junit5适配器 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.5.0</version>
</dependency>
2 Mockito 主要注解介绍
publicclassA{
private B b; // 完全mock
private C c; // 需要执行方法
// 需要执行真实方法 D d;
publicvoidfunc(){
}
}
classC{
private B b;
publicvoidneedExec(){
}
}
publicclassB{
}
@InjectMocks注解
被测试类,这种很容易理解,我们测试这个类,当然也需要向其注入bean。比如上面的A 被测试类中的,需要执行其真实的方法,但其里面也要主要bean,也就是上面的C,我们需要测试neeExec方法,但我们不关系B的具体细节。现实中比如事物,并发锁等。这一类需要Mockito.spy(new C())的形式,不然会报错
@Mock
@Spy
(MockitoExtension.class)
publicclassATest{
private A a=new A();
private B b;
private D d;
private C c= Mockito.spy(new C());;
publicvoidsetUp()throws Exception {
MockitoAnnotations.openMocks(this);
}
"/com/alibaba/cq/springtest/jcode5test/needMockService/A/func.json"}) (strings = {
publicvoidfuncTest(String str){
JSONObject arg= TestUtils.getTestArg(str);
a.func();
//todo verify the result
}
}
3 Mockito和junit5常见问题
1. mock静态方法
2. Mockito版本和java版本兼容问题
Mockito cannot mock thisclass: xxx
Mockito can only mock non-private & non-final classes.
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.12.6</version>
</dependency>
3. Jupiter-api版本兼容问题
Process finished withexit code 255
java.lang.NoSuchMethodError: org.junit.jupiter.api.extension.ExtensionContext.getRequiredTestInstances()Lorg/junit/jupiter/api/extension/TestInstance
四 测试代码自动生成
1 业界和集团方案调研
publicclassBaseTest {
protected TestService testService;
public String baseTest() {
return testService.testBase(1); // 4
}
}
publicclassJCode5extendsBaseTest {
publicvoidtestExtend(){
String s = testService.testOther(new Student()); //1
// 调用 另一个方法
System.out.println(testBean());
// 调用基类方法
baseTest();
}
// 使用testService
public String testBean() {
testService.testMuti(new ArrayList<Integer>() {{add(1);}}, 2); //2
return testService.getStr(12); //3
}
/**
* 测试范型类
*/
publicvoidtestGeneric(Person person) {
//test
list.stream().forEach(a -> {
System.out.println(a);
});
for (int i = 0; i < 2; i++) {
Long aLong = testService.getLong("1213"
, "12323");
System.out.println(aLong);
}
System.out.println(testBean());
}
}
publicclassTestService {
public String testBase(Integer integer) {
return"TestBase";
}
public List<String> testMuti(List<Integer> a, Integer c) {
List<String> res = new ArrayList<>();
res.add(a.toString() + c + "test muti");
return res;
}
public String getStr(Integer integer) {
return"TestService" + getInt();
}
public String testOther(Student student) {
return student.getAge() + "age";
}
}
1、TestMe
@Test
void testTestExtend() {
when(testService.getStr(anyInt())).thenReturn("getStrResponse");
when(testService.testMuti(any(), anyInt())).thenReturn(Arrays.<String>asList("String"));
when(testService.testOther(any())).thenReturn("testOtherResponse");
jCode5.testExtend(Integer.valueOf(0));
}
@Test
void testTestGeneric() {
when(testService.getStr(anyInt())).thenReturn("getStrResponse");
when(testService.getLong(anyString(), anyString())).thenReturn(Long.valueOf(1));
when(testService.testMuti(any(), anyInt())).thenReturn(Arrays.<String>asList("String"));
jCode5.testGeneric(newPerson());
}
2、JunitGenerate
publicvoidtestTestExtend()throws Exception {
//TODO: Test goes here...
}
3、Squaretest
五 打造代码自动生成最佳方案
1 插件安装
2 插件使用
生成测试代码,也就是生成单元测试。 生成json数据,通常用来生成测试数据,比如model。用来参数化测试。 增加测试方法,随着业务开发,类可能增加一下功能方法,这个时候相应的可以增加测试方法
1、生成测试类
2、生成的结果---类+json数据
"/com/cq/common/JCode5/testExtend.json"}) (strings = {
publicvoid testExtendTest(String str) {
JSONObject arg= TestUtils.getTestArg(str);
Integer i = arg.getInteger("Integer");
// 识别泛型活着集合类
List<String> stringList = JSONObject.parseArray(arg.getString("List<String>"),String.class);
String stringArg = arg.getString("String");
String stringArg1 = arg.getString("String");
String stringArg0 = arg.getString("String");
// 识别四个方法,包括父类调用、其他方法调用
when(testService.testBase(any(Integer.class))).thenReturn(stringArg);
when(testService.testMuti(any(List.class),any(Integer.class))).thenReturn(stringList);
when(testService.getStr(any(Integer.class))).thenReturn(stringArg0);
when(testService.testOther(any(Student.class))).thenReturn(stringArg1);
jCode5.testExtend(i);
//todo verify the result
}
{
"Integer":1,
"String":"test",
"List<String>":[
"test"
]
}
3、补充判定语句
注意事项
该插件生成的代码需要junit5和mockito的支持,使用时需要引入相关的依赖 增加assert校验逻辑,看是不是想要的结果,目前插件不会自动生成assertEquals等断言代码。 运用参数化测试能力,复制一份生成的json文件并修改输入数据,多组测试
3 插件实现介绍
4 后期规划
mock数据可定制,目前的想法是 固定值比如目前的String: test、Integer和boolean: 0、1 测试者使用配置模版,比如txt文件包含keyValue对 使用Faker,对于name、email、phone这种特定倾向的数据进行特色自动生成 自动分支测试,这一块的想法目前主要针对if来做,需要一定的时间。 其他
六 写在最后
Elasticsearch实战进阶营
点击阅读原文查看详情
阅读原文 关键词
方法
容器
数据
代码
单元测试
最新评论
推荐文章
作者最新文章
你可能感兴趣的文章
Copyright Disclaimer: The copyright of contents (including texts, images, videos and audios) posted above belong to the User who shared or the third-party website which the User shared from. If you found your copyright have been infringed, please send a DMCA takedown notice to [email protected]. For more detail of the source, please click on the button "Read Original Post" below. For other communications, please send to [email protected].
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。