一、背景

很多公司对分支单测覆盖率会有一定的要求,比如 单测覆盖率要达到 60% 或者 80%才可以发布。

有时候工期相对紧张,就优先开发功能,测试功能,然后再去补单元测试。

但是编写单元测试又比较浪费时间,有没有能够很大程度上自动化生成单元测试的插件,自己简单改改即可呢?

自己尝试在 Idea 插件库里搜索相关插件并去尝试使用,发现 TestMe 还可以。后面和其他同学交流,谎伴 同学推荐他一直在用的 Squaretest,我试用之后发现相当不错。

在这里简单介绍这两个插件。


如果先尝试其他单元测试相关插件,可以在 IDEA 里 或者点这里:
https://plugins.jetbrains.com/search?orderBy=downloads&tags=Unit%20testing

二、推荐工具

2.1 Squaretest

2.1.1 使用介绍

官网地址:https://squaretest.com/
官方用户手册:https://squaretest.com/#user_guide
官网插件地址
https://plugins.jetbrains.com/plugin/10405-squaretest

优点:生成的代码比较规整,生成的代码比较,帮助构造一些参数等。
缺点:不使用 Confirm Mock功能时,对Spring 的 Bean 生成单测代码时,如果属性是通过 @Setter 注解注入,则不会生成 @Mock 属性 ;如果想实现暂时只能自己修改模板来支持(后面会给出)。

使用方法:
可以在顶部菜单 [Squaretest] 菜单中选择第一个或者使用对应快捷键创建单元测试。

生成的代码:

这个例子比较简单,只是给大家演示如何使用,实际使用中类复杂时,就能体会到该插件的强大。

示例代码:

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;@Service
public class UserManager {@Resourceprivate UserDAO userDAO;public List<UserDO> someThing(Param param) {List<UserDO> result = new ArrayList<>();if(param == null) {return result;}List<String> userIds = param.getUserIds();if(CollectionUtils.isEmpty(userIds)) {return result;}List<UserDO> users = userDAO.findByIds(userIds);if(CollectionUtils.isEmpty(users)) {return result;}return  users.stream().filter(UserDO::getCanShow).collect(Collectors.toList());}}

生成的代码:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;import java.util.Arrays;
import java.util.Collections;
import java.util.List;import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;@RunWith(MockitoJUnitRunner.class)
public class UserManagerTest {@Mockprivate UserDAO mockUserDAO;@InjectMocksprivate UserManager userManagerUnderTest;@Testpublic void testSomeThing() {// Setupfinal Param param = new Param();param.setUserIds(Arrays.asList("value"));param.setOthers("others");// Configure UserDAO.findByIds(...).final UserDO userDO = new UserDO();userDO.setCanShow(false);userDO.setName("name");final List<UserDO> userDOS = Arrays.asList(userDO);when(mockUserDAO.findByIds(Arrays.asList("value"))).thenReturn(userDOS);// Run the testfinal List<UserDO> result = userManagerUnderTest.someThing(param);// Verify the results}@Testpublic void testSomeThing_UserDAOReturnsNoItems() {// Setupfinal Param param = new Param();param.setUserIds(Arrays.asList("value"));param.setOthers("others");when(mockUserDAO.findByIds(Arrays.asList("value"))).thenReturn(Collections.emptyList());// Run the testfinal List<UserDO> result = userManagerUnderTest.someThing(param);// Verify the resultsassertEquals(Collections.emptyList(), result);}
}

官方演示1:选择性生成测试代码
官方演示2:选择需要 mock 的属性

官方示例3:在单测里写 test 即可选择需要测试的方法自动生成测试代码

2.2.2 定制化

前面讲到默认的模板,对Spring 的 Bean 生成单测代码时,如果通过 xml 方式声明 bean ,属性都是通过 @Setter 注解注入,则不会生成 @Mock 属性 。

public class UserManager {@Setterprivate UserDAO userDAO;public List<UserDO> someThing(Param param) {// 省略}}

可以使用 Confirm Mocks 功能选择该属性需要 Mock


该插件也支持对生成的模板进行调整:

还可以对模板进行简单修改,所有 @Setter 都会自动加上 @Mock 注解:

1526 行:


在依赖的注解属性中添加 Setter 注解即可。

  ## Add the simple names or cannonical names of any custom dependency annotations to the method call below.#set($dependencyAnnotatedFields = $sourceClass.fieldsAnnotatedWith('Inject', 'Setter','Autowired', 'Resource', 'PersistenceContext'))

如果使用 powermock ,需要进行修改 1502 -1506 行:

#set($mockitoRunnerCanonicalName = 'org.powermock.modules.junit4.PowerMockRunner')
#set($mockitoRunnerName = 'PowerMockRunner')

删除 部分

    #if(!$ClassUtils.isInTestClasspath('org.mockito.junit.MockitoJUnitRunner') && $ClassUtils.isInTestClasspath('org.mockito.runners.MockitoJUnitRunner'))#set($mockitoRunnerCanonicalName = 'org.mockito.runners.MockitoJUnitRunner')#end

2.2 TestMe

2.2.1 使用介绍

插件官网地址
https://plugins.jetbrains.com/plugin/9471-testme

功能:

自动生成 Java JUnit 4/5, TestNG 单元测试
自动生成 Mockito mocks
自动生成 测试参数和断言语句
自动生成相关 mock 方法
IDEA 菜单: Code->TestMe, Code->Generate

优点:Spring 的 Bean 生成单测代码时,即使 @Component 这类注解标注,属性通过 Setter 注解注入时,也会自动给添加 @Mock 和 @InjectMock 这类属性。
缺点:默认模板会在生成的方法上都加上 throws Exception

示例代码1:

或者直接使用快捷键

生成的代码:

这个例子比较简单,只是给大家演示如何使用,实际使用中类复杂时,就能体会到该插件的强大。

示例代码2:

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;@Service
public class UserManager {@Resourceprivate UserDAO userDAO;public List<UserDO> someThing(Param param) {List<UserDO> result = new ArrayList<>();if(param == null) {return result;}List<String> userIds = param.getUserIds();if(CollectionUtils.isEmpty(userIds)) {return result;}List<UserDO> users = userDAO.findByIds(userIds);if(CollectionUtils.isEmpty(users)) {return result;}return  users.stream().filter(UserDO::getCanShow).collect(Collectors.toList());}}

生成的代码

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;import java.util.Arrays;
import java.util.List;import static org.mockito.Mockito.*;public class UserManagerTest {@MockUserDAO userDAO;@InjectMocksUserManager userManager;@Beforepublic void setUp() {MockitoAnnotations.initMocks(this);}@Testpublic void testSomeThing() throws Exception {when(userDAO.findByIds(any())).thenReturn(Arrays.<UserDO>asList(new UserDO()));List<UserDO> result = userManager.someThing(new Param());Assert.assertEquals(Arrays.<UserDO>asList(new UserDO()), result);}
}//Generated with love by TestMe :) Please report issues and submit feature requests at: http://weirddev.com/forum#!/testme

自己在此基础上简单修改即可。

2.2.2 定制化

大家还可以根据自己需要对模板进行修改:

默认模板存在几个问题:
1、没有在类上增加 @RunWith(MockitoJUnitRunner.class) 注解
2、单元测试方法后面默认会带上 throws Exception 没有太大必要
3、底部 TestMe Footer.java 的内容不需要
4、@Mock 和 @InjectMock 之间没空行

对 Junit4 & mockito 复制一份(原始文件是只读的)进行修改

生成一个 Copy of Junit4 & mockito 的模板,可以对其进行修改

修改后的模板:

#parse("Copy of TestMe macros.java")
#set($hasMocks=$MockitoMockBuilder.hasMockable($TESTED_CLASS.fields))
#if($PACKAGE_NAME)
package ${PACKAGE_NAME};
#endimport static org.junit.Assert.*;
import org.junit.Test;
#if($hasMocks)
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.junit.Assert;
//import static org.mockito.Mockito.*;
#end#parse("File Header.java")
@RunWith(MockitoJUnitRunner.class)
public class ${CLASS_NAME} {#renderMockedFields($TESTED_CLASS.fields)#renderTestSubjectInit($TESTED_CLASS,$TestSubjectUtils.hasTestableInstanceMethod($TESTED_CLASS.methods),$hasMocks)
#if($hasMocks)@Beforepublic void setUp() {}
#end
#foreach($method in $TESTED_CLASS.methods)
#if($TestSubjectUtils.shouldBeTested($method))@Testpublic void #renderTestMethodName($method.name)(){#if($MockitoMockBuilder.shouldStub($method,$TESTED_CLASS.fields))
#renderMockStubs($method,$TESTED_CLASS.fields)#end#renderMethodCall($method,$TESTED_CLASS.name)
#if($method.hasReturn())        #renderJUnitAssert($method)#end}
#end
#end
}

然后我们生成单元测试时选择该模板:

发现生成的代码格式好了不少:

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;import java.util.Arrays;
import java.util.List;import static org.junit.Assert.assertEquals;import static org.mockito.Mockito.*;@RunWith(MockitoJUnitRunner.class)
public class UserManagerTest {@MockUserDAO userDAO;@InjectMocksUserManager userManager;@Beforepublic void setUp() {}@Testpublic void testSomeThing() {when(userDAO.findByIds(any())).thenReturn(Arrays.<UserDO>asList(new UserDO()));List<UserDO> result = userManager.someThing(new Param());assertEquals(Arrays.<UserDO>asList(new UserDO()), result);}
}

三、单测高效构造参数和返回值神器

我们还可以借助其他工具,自动生成测试的参数或者返回值。
https://github.com/j-easy/easy-random

可以参考我之前的一篇文章:
《Java高效构造对象的神器:easy-random 简介》

一两行就可以构造一个非常复杂的对象或者对象列表。

《Java 单元测试生成测试字符串的神器:java-faker》

如果我们想要随机构造人名、地名、天气、学校、颜色、职业,甚至符合某正则表达式的字符串

四、总结

灵活使用单元测试自动生成插件,可以节省很多时间。

大家可以安装并试用这两个插件,然后根据自己的喜好,选择最适合自己的那个插件使用。
也可以根据自己的喜好,对模板进行调整。
此外,大家不要对插件要求太高,生成的单元测试或多或少还是需要自己进行简单修改,如修改下参数、增加几个断言等。


创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。

告别加班/解放双手提高单测覆盖率之Java 自动生成单测代码神器推荐相关推荐

  1. 解放程序员双手!GPT-3自动生成SQL语句 | 代码开源

    金磊 发自 凹非寺 量子位 报道 | 公众号 QbitAI "无所不能"的GPT-3,现在又来解放程序员们的双手了. 像这样,只需用简单的英文问下GPT-3"上个月注册了 ...

  2. 买定离手!AI预测英雄联盟S12冠军;微软使用AI提高农业生产效率;编程语言的自动生成;机器学习核方法入门·电子书;前沿论文 | ShowMeAI资讯日报

  3. 什么是外汇跟单?外汇MT4自动跟单系统靠谱吗?

    对于很多刚刚进入外汇市场的新手投资者而言,必须踏踏实实学习基础知识和交易技巧,才能在外汇市场获取盈利,因为在没有任何专业知识的情况下,想要在外汇市场盈利是一件非常艰难的事情.而一些脑洞大开的外汇新手投 ...

  4. 【客户下单】后台系统自动分单成功生成工单发送短信

    [客户下单]后台系统自动分单成功生成工单发送短信 自动分单成功,后台生成工单发送短信,否则进入人工分单流程. 生成工单,发送短信的方法抽取: //抽取的方法:生成工单 发送短信 private voi ...

  5. 【客户下单】后台系统匹配分区关键字实现自动分单

    [客户下单]后台系统基于分区关键字匹配实现自动分单 通过前端传递过来的"省市区",找到区域,在通过区域找到分区,将客户发货地址与分区的关键字进行匹配,如果包含分区的关键字或辅助关键 ...

  6. 【客户下单】基于CRM完全匹配地址库实现自动分单

    [客户下单]基于CRM完全匹配地址库实现自动分单 将寄件人的下单地址,以webservice的方式,发送给CRM地址库,获取到对应的客户关联的定区,匹配与定区关联的快递员,实现自动分单. 在crm_m ...

  7. sql server小型案例-自动生成销售单号的触发器

    sql server实现自动生成销售单号的触发器 1.有关系统 最近在做信息系统课程设计的小项目,我们团队所做的是一个简单的自动贩卖机销售管理系统,其中我负责的部分是销售管理,其中需要实现的一个功能是 ...

  8. Java使用FreeMarker自动生成Word文档(带图片和表单)

    Java使用FreeMarker自动生成Word文档(带图片和表单) 1 背景 2 目标效果 3 创建Word模板 3.1 创建模板文档 3.2 转换模板文档 3.3 处理模板文档中的占位符 3.4 ...

  9. vue注册全局方法:生成单号------年月日(4+2+2)+随机数n位 (前端生成单号,从接口取单号)

    vue注册全局方法:生成单号------年月日(4+2+2)+随机数n位 1.写方法 2.全局注册 3.页面中使用此方法 1.写方法 因为再vue中多次用到此方法,故而创建一个公用的文件内含多次被调用 ...

最新文章

  1. mysql mysqlhotcopy_MySQL备份之mysqlhotcopy与注意事项
  2. Android日志打印类LogUtils,能够定位到类名,方法名以及出现错误的行数并保存日志文件...
  3. leetcode算法题--反转字符串
  4. Python字符串格式化之format方法详解
  5. pyspark--dataframe使用
  6. Kalman Filter 递归算法
  7. 想给视频去色并加马赛克就用它
  8. 贪吃蛇贪吃蛇代码--c语言版 visual c++6.0打开
  9. vivado 下载程序出错:ERROR: [Labtools 27-3165] End of startup status: LOW
  10. linux服务器双网卡路由优先级冲突 Metric值
  11. Excel如何将英文前的中文全部提取出来
  12. Java oss 上传图片视频
  13. 《dota2》地精修补匠tinker路人攻略
  14. Kotlin: Java 6 废土中的一线希望
  15. 国家信息系统安全等级保护基本要求——等保一级、二级、三级、四级内容
  16. 病原微生物高通量测序:第三节 检测原理
  17. java数组和字符串相互转换
  18. 搭建多节点Fabric网络(Windows系统)
  19. 刘润年度演讲2022:进化的力量(演讲全文)
  20. 时间选择器抽出。PickerView和原生

热门文章

  1. 数据库监控工具及相关知识
  2. 5.形容词性从句的起源与本质
  3. python 列表切片负值,详解Python 切片语法
  4. 基于web的网上考试报名系统
  5. win10运行在哪里_win10文件夹同步,教你2种方法
  6. php layer弹出层更改背景,详解Layer弹出层样式
  7. input宽度随内容变化
  8. C++解析multipart/form-data
  9. web项目服务器拒绝了连接,Websocket服务器连接被拒绝
  10. 运维自动化之Ansible 和 HTTP协议和APACHE