百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

一款自动生成单元测试的 IDEA 插件

lipiwang 2024-12-01 00:46 7 浏览 0 评论

今天来介绍一款工具Squaretest,它是一款自动生成单元测试的插件,为什么会用到它?

主要因为最近公司上了代码质量管控的指标,会考评各个项目的单元测试覆盖率,以及sonar扫描出来的各种问题,很多老项目老代码,或者着急交付的项目,单元测试严重缺失,覆盖率只有5%不到。

所以几个小伙伴这几天就在疯狂的堆单元测试,3个人堆了2天才堆到30%,于是我也来上手帮忙写了两个,写到第二个的时候就发现,这个活不应该是人干的,要去看原来的代码,然后根据逻辑写各种Mock,感觉是有迹可循的东西,所以就查了下,发现果然有插件帮我们来干这个事情,那么解下来就来看看。

我使用的是idea,我们先来下载一下插件,File——>Settings——>Plugins,搜索Squaretest,然后install就好了,插件安装完成后需要重启一下

重启之后,菜单栏就多了一项Squaretest,下面我们来讲下怎么用,大家也可以通过看这个菜单的最后一项:Generate Test Methods(Help)来看它的一个演示,但演示不太全,我下面截图给大家看下我怎么用的,以及一些使用心得。


首先我们打开一个类,这个类就是我们即将要作为实验的类,这个类有7个public方法,因为Squaretest生成的单元测试方法都是只能生成public的,当然这也是合理的嘛!毕竟private的肯定被public调用了。


如果我们来手写这个类的单元测试,光看都要一会,下面看我操作,打开你的类,光标定位到代码里,右击鼠标选择Generate…


然后你就会看到这里有两个熟悉的图标,第一次的话选择第二个选项,它会让你选择你一下单元测试的模板,因为我已经选择过了,所以我现在演示不回再弹出,但后面我会告诉你怎么更改模板。


选择第二项后就会弹出一个框看下面这里它自动会识别出当前类需要Mock的成员变量,直接点ok


自动会使用类的真实目录层次在test文件夹中创建出来一个单元测试类,类名就是原类名后加Test


我把代码贴出来给大家看看它生成出来的是什么样的,看看吓不吓人,牛逼牛逼,7个单元测试方法,秒秒钟就出来了,各位看官你们自己写要多久能写出来,毕竟时间就是金钱啊!然后我们执行一把试试!

public class CrawlerScreenShotServiceImplTest {

@Mock
private CrawerScreenShotTaskMapper mockCrawerScreenShotTaskMapper;
@Mock
private CrawerScreenShotTaskLogMapper mockCrawerScreenShotTaskLogMapper;

@InjectMocks
private CrawlerScreenShotServiceImpl crawlerScreenShotServiceImplUnderTest;

@Before
public void setUp() {
initMocks(this);
}

@Test
public void testReceiveData() {
// Setup
final CrawlerScreenShotVO vo = new CrawlerScreenShotVO();
vo.setUrl("url");
vo.setPcFlag(false);
vo.setMembergroup("membergroup");
vo.setTaskType(0);
vo.setUrlType(0);

when(mockCrawerScreenShotTaskLogMapper.saveSelective(any(CrawerScreenShotTaskLog.class))).thenReturn(0);
when(mockCrawerScreenShotTaskMapper.saveBatch(Arrays.asList(new CrawlerScreenShotTask(0L, "url", "imageOssUrl", false, false, "memberGroup", 0, 0, "fileName", new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime(), new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime(), false, "skuCode", "state", "operater")))).thenReturn(0);

// Run the test
final Result<String> result = crawlerScreenShotServiceImplUnderTest.receiveData(vo);

// Verify the results
}

@Test
public void testListJobScreenShotTask() {
// Setup

// Configure CrawerScreenShotTaskMapper.listJobScreenShotTask(...).
final CrawlerScreenShotTaskDto crawlerScreenShotTaskDto = new CrawlerScreenShotTaskDto();
crawlerScreenShotTaskDto.setId(0L);
crawlerScreenShotTaskDto.setUrl("url");
crawlerScreenShotTaskDto.setSkuCode("skuCode");
crawlerScreenShotTaskDto.setPcFlag(false);
crawlerScreenShotTaskDto.setMemberGroup("memberGroup");
crawlerScreenShotTaskDto.setUrlType(0);
crawlerScreenShotTaskDto.setFileName("fileName");
crawlerScreenShotTaskDto.setTaskType(0);
crawlerScreenShotTaskDto.setState("state");
final List<CrawlerScreenShotTaskDto> crawlerScreenShotTaskDtos = Arrays.asList(crawlerScreenShotTaskDto);
when(mockCrawerScreenShotTaskMapper.listJobScreenShotTask(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime())).thenReturn(crawlerScreenShotTaskDtos);

// Run the test
final List<CrawlerScreenShotTaskDto> result = crawlerScreenShotServiceImplUnderTest.listJobScreenShotTask();

// Verify the results
}

@Test
public void testQuery() {
// Setup
final NikeScreenShotListRequestVo requestVo = new NikeScreenShotListRequestVo();
requestVo.setUrl("url");
requestVo.setUrlType(0);
requestVo.setStartTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());
requestVo.setEndTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());
requestVo.setStatus(0);
requestVo.setPcFlag(0);
requestVo.setPageNum(0);
requestVo.setPageSize(0);

// Configure CrawerScreenShotTaskMapper.query(...).
final PimScreenShotVo pimScreenShotVo = new PimScreenShotVo();
pimScreenShotVo.setId(0L);
pimScreenShotVo.setUrl("url");
pimScreenShotVo.setImageOssUrl("imageOssUrl");
pimScreenShotVo.setStatus(0);
pimScreenShotVo.setPcFlag(false);
pimScreenShotVo.setCreateTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());
pimScreenShotVo.setUrlType(0);
pimScreenShotVo.setMsg("msg");
final List<PimScreenShotVo> pimScreenShotVos = Arrays.asList(pimScreenShotVo);
when(mockCrawerScreenShotTaskMapper.query(any(NikeScreenShotListRequestVo.class))).thenReturn(pimScreenShotVos);

// Run the test
final PageInfo<PimScreenShotVo> result = crawlerScreenShotServiceImplUnderTest.query(requestVo);

// Verify the results
}

@Test
public void testQuerySelectBoxData() {
// Setup

// Configure CrawerScreenShotTaskMapper.query(...).
final PimScreenShotVo pimScreenShotVo = new PimScreenShotVo();
pimScreenShotVo.setId(0L);
pimScreenShotVo.setUrl("url");
pimScreenShotVo.setImageOssUrl("imageOssUrl");
pimScreenShotVo.setStatus(0);
pimScreenShotVo.setPcFlag(false);
pimScreenShotVo.setCreateTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());
pimScreenShotVo.setUrlType(0);
pimScreenShotVo.setMsg("msg");
final List<PimScreenShotVo> pimScreenShotVos = Arrays.asList(pimScreenShotVo);
when(mockCrawerScreenShotTaskMapper.query(any(NikeScreenShotListRequestVo.class))).thenReturn(pimScreenShotVos);

// Run the test
final PimScreenShotTaskParamsDto result = crawlerScreenShotServiceImplUnderTest.querySelectBoxData();

// Verify the results
}

@Test
public void testFindExecutionScreenShotTaskCount() {
// Setup
when(mockCrawerScreenShotTaskMapper.findExecutionScreenShotTaskCount()).thenReturn(0);

// Run the test
final Integer result = crawlerScreenShotServiceImplUnderTest.findExecutionScreenShotTaskCount();

// Verify the results
assertEquals(0, result);
}

@Test
public void testFindCrawerScreenshotTaskByCreateTime() {
// Setup
final CrawlerScreenShotTaskSyncDto crawlerScreenShotTaskSyncDto = new CrawlerScreenShotTaskSyncDto();
crawlerScreenShotTaskSyncDto.setId(0L);
crawlerScreenShotTaskSyncDto.setUrl("url");
crawlerScreenShotTaskSyncDto.setSkuCode("skuCode");
crawlerScreenShotTaskSyncDto.setTaskType(0);
crawlerScreenShotTaskSyncDto.setStatus(0);
crawlerScreenShotTaskSyncDto.setLastModifyTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());
crawlerScreenShotTaskSyncDto.setOperater("operater");
crawlerScreenShotTaskSyncDto.setMsg("msg");
final List<CrawlerScreenShotTaskSyncDto> expectedResult = Arrays.asList(crawlerScreenShotTaskSyncDto);

// Configure CrawerScreenShotTaskMapper.findCrawerScreenshotTaskByCreateTime(...).
final CrawlerScreenShotTaskSyncDto crawlerScreenShotTaskSyncDto1 = new CrawlerScreenShotTaskSyncDto();
crawlerScreenShotTaskSyncDto1.setId(0L);
crawlerScreenShotTaskSyncDto1.setUrl("url");
crawlerScreenShotTaskSyncDto1.setSkuCode("skuCode");
crawlerScreenShotTaskSyncDto1.setTaskType(0);
crawlerScreenShotTaskSyncDto1.setStatus(0);
crawlerScreenShotTaskSyncDto1.setLastModifyTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());
crawlerScreenShotTaskSyncDto1.setOperater("operater");
crawlerScreenShotTaskSyncDto1.setMsg("msg");
final List<CrawlerScreenShotTaskSyncDto> crawlerScreenShotTaskSyncDtos = Arrays.asList(crawlerScreenShotTaskSyncDto1);
when(mockCrawerScreenShotTaskMapper.findCrawerScreenshotTaskByCreateTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime())).thenReturn(crawlerScreenShotTaskSyncDtos);

// Run the test
final List<CrawlerScreenShotTaskSyncDto> result = crawlerScreenShotServiceImplUnderTest.findCrawerScreenshotTaskByCreateTime(new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());

// Verify the results
assertEquals(expectedResult, result);
}

@Test
public void testQueryCrawlerDashboard() {
// Setup
when(mockCrawerScreenShotTaskMapper.queryCrawlerDashboard(0, 0, 0, new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime(), new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime())).thenReturn(0);

// Run the test
final Integer result = crawlerScreenShotServiceImplUnderTest.queryCrawlerDashboard(0, 0, 0, new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime(), new GregorianCalendar(2019, Calendar.JANUARY, 1).getTime());

// Verify the results
assertEquals(0, result);
}
}

报错了呢,不要慌,这个断言是为了检查你单元测试跑出来的结果是否符合预期的,如果你不想检查只想完成覆盖率,直接干掉就可以了(手动狗头)。

怎么样!刺不刺激,爽不爽,秒秒钟90多行的代码覆盖率就到了90%以上.


上面说过第一次进来会让你选择单元测试的模板,如果你要切换的话可以在单元测试类中按快捷键,Alt+M,或者通过Squaretest的菜单倒数第二个,下面这个就是按快捷键的效果,我选择的是这个模板,你们也可以借鉴。


OK,以上Squaretest部分就结束了,当然拉也不能高兴的太早,这个类算是比较成功的情况,很多时候还是要你自己小修小改的,毕竟它生成出来的测试数据可能完全匹配不上你的if else数据对吧,但这都很好改啊,这样就从自己分析if else变成了,debug程序了呀,哪里报错,debug过去,看看是不是生成的数据有问题,改个数据,就通过了,反正本人用的是很舒畅的,妥妥的节省70%的工作量。

解决了上面一个问题之后,又发现另一个问题,这个工具VO,DTO,Entity,Command,Model这种实体类来讲,一般这种实体类我们都用lombok的注解get,set,还有constract构造器等注解,但是这个工具只能生成这些实体类的构造器的单元测试,无法生成get set方法的单元测试,所以写了个base方法,实体类继承一下,简单的写两行带就好了,看下面代码:

@SpringBootTest
@RunWith(MockitoJUnitRunner.class)
public abstract class BaseVoEntityTest<T>
{
protected abstract T getT();

private void testGetAndSet() throws IllegalAccessException, InstantiationException, IntrospectionException,
InvocationTargetException
{
T t = getT();
Class modelClass = t.getClass();
Object obj = modelClass.newInstance();
Field[] fields = modelClass.getDeclaredFields();
for (Field f : fields) {
boolean isStatic = Modifier.isStatic(f.getModifiers());
// 过滤字段
if (f.getName().equals("isSerialVersionUID") || f.getName().equals("serialVersionUID") || isStatic || f.getGenericType().toString().equals("boolean")
|| f.isSynthetic()) {
continue;
}
PropertyDescriptor pd = new PropertyDescriptor(f.getName(), modelClass);
Method get = pd.getReadMethod();
Method set = pd.getWriteMethod();
set.invoke(obj, get.invoke(obj));
}
}

@Test
public void getAndSetTest() throws InvocationTargetException, IntrospectionException,
InstantiationException, IllegalAccessException
{
this.testGetAndSet();
}

}

同样的方式我们在实体类上通过Squaretest生成单元测试,然后继承我上面写的那个base类,vo的单元测试代码稍加改动,如下

看run完之后,覆盖率100%,妥妥的,通过这两个解决方案,一天之内我们就把覆盖率搞到了60%以上,不要太刺激,大家可以用用试试哦,当然这个也不是纯为了应付差事写的单元测试,我们后续开发的时候,也可以用这个工具来生成,然后自测自己的代码,这样也是提升工作效率的嘛!


来源:blog.csdn.net/sun5769675/article/details/111043213

相关推荐

怀孕生孩子那些事 之 小剂量阿司匹林为什么是81mg?

作者:段涛无论是在内科还是妇产科,阿司匹林都是常用药物。在看国外文献以及药物说明书的时候,或者是在海外代购药物的时候,大家都会有一些小小的疑问,就是为什么这些国外阿司匹林的剂量经常不是整数?阿司匹林剂...

国际临床试验发现 将三种心脏病药物组合成一粒药丸更有效果

一项大型国际临床试验发现,与分别服用每种药物相比,将三种常见的心脏病药物组合成一粒药丸在预防心血管事件和死亡方面明显更有效。在新试验中测试的治疗方法称之为Trinomia,使用由3种药物混合而成...

如何看懂医生写的处方字?

解码处方:从“天书”到清晰指引当我们拿到医生开具的处方,面对那密密麻麻、看似潦草的字迹,往往会感到困惑。医生的处方字似乎自成一体,宛如神秘的“天书”,但这背后其实是有迹可循的,只要掌握了正确的方法,...

当归全身都是宝!一碗黄芪当归补血汤,打造红润好气色,收藏备用

当归别名秦归、云归,外形类似人参,是伞形科植物当归的干燥根,《本草纲目》提到,“古人娶妻为嗣续也,当归调血为女人要药,为思夫之意,故有当归之名。”从当归鸭、当归羊肉到中药四物汤、生化汤,当归食药两...

14张菌落图&quot;撞脸&quot;他人文献!贵州某三甲医院研究成果遭质疑

2019年5月,贵州省人民医院在TranslationalCancerResearch(IF1.4999/4区)期刊上发表了一篇论文。在发表6年后,因图片与早期论文图片重复在pubpeer上被读者...

阿司匹林的各种剂型,有什么区别吗?听医生说说看

无论是在内科还是妇产科,阿司匹林都是常用药物。为什么这些国外阿司匹林的剂量经常不是整数?在看国外文献以及药物说明书的时候,或者是在海外代购药物的时候,大家都会有一些小小的疑问,就是为什么这些国外阿司匹...

医生讲解如何正确应用阿司匹林,控制冠心病的发展进程,改善症状

谈及冠心病的临床控制和干预,永远绕不开阿司匹林,它是冠心病患者最熟悉的药物,绝大多数病人都吃过阿司匹林,可以说是控制冠心病的基石,贯穿了患者的前中后期,需要特别慎重。阿司匹林是什么?阿司匹林(Aspi...

测绘专业!EI会议选刊口诀必看!

推荐EI会议会议名称:IEEE国际地理信息与遥感技术研讨会(IGIRS)截稿时间:即日起至11月30日时间地点:次年3月15-17日·新加坡论文集上线:会后2个月内提交至IEEEXplore版...

解码运营诉求:一对一直播源码如何实现&quot;用户增长到付费转化&quot;?

在竞争激烈的一对一直播交友赛道,用户增长和付费转化是决定产品成败的两大核心指标。然而,许多团队面临一个共同困境:用户进来了,但留不住、难付费。问题的根源往往在于源码架构是否深度适配运营需求。山东布谷科...

计算机EI源刊论文投稿秘籍|审稿周期解析

期刊推荐:《IEEETransactionsonComputers》-刊号:ISSN0018-9340-影响因子:3.7(最新数据)-分区:中科院2区,JCRQ1-版面费:非开源免...

学习人工智能AI所需的数学基础

人工智能(AI)的快速发展离不开数学理论的有力支撑。不管是构建神经网络模型,亦或是优化算法性能,乃至处理高维数据的不确定性,数学皆充当着核心要角。此文将自核心数学领域起始,详尽剖析其与AI的关联所...

化学工程亲测!EI会议高通过率

高录用率EI会议推荐会议名称:InternationalConferenceonChemicalEngineeringandApplications(CCEA)会议编号:CCF-C...

DDD编程入门指南

什么是DDD编程?领域驱动设计(Domain-DrivenDesign,简称DDD)是一种软件开发方法论,由EricEvans在其2003年的著作《领域驱动设计:软件核心复杂性应对之道》中提出。D...

软件工程?EI会议录用潜规则!

【推荐会议】ICSEA2025(会议号:61205)截稿时间:2025年4月10日召开时间与地点:2025年8月22-24日·美国波士顿论文集上线:会后3个月内提交至SpringerLN...

安全漏洞之经典上传漏洞

0x01概要说明文件上传漏洞可以说是日常渗透测试用得最多的一个漏洞,因为用它获得服务器权限最快最直接。但是想真正把这个漏洞利用好却不那么容易,其中有很多技巧,也有很多需要掌握的知识。俗话说,知己知彼...

取消回复欢迎 发表评论: