一款自动生成单元测试的 IDEA 插件,开发效率提升 70% 以上!

简介: 一款自动生成单元测试的 IDEA 插件,开发效率提升 70% 以上!

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


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


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


另外,更多 IDEA 好用的插件全部整理好了,微信搜索Java技术栈,在后台发送:工具,可以在线阅读。


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


1.png


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


2.png


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


3.png


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


4.png


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


5.png


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


6.png


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


7.png


我把代码贴出来给大家看看它生成出来的是什么样的,看看吓不吓人,牛逼牛逼,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);
    }
}


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


8.png


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


9.png


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


10.png


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的单元测试代码稍加改动,如下


11.png


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


12.png


觉得不错就用起来吧!


相关文章
|
4天前
|
JSON Java Maven
IDEA开发过程中常用插件必备
本文介绍了提升IntelliJ IDEA开发效率的几个插件:Codota用于强大代码补全;Auto filling Java call arguments自动填充函数参数,节省手动填充时间;GsonFormat能快速将JSON转换为Java实体类;Rainbow Brackets帮助识别括号所属代码块;Maven Helper提供高效查看和分析Maven依赖树功能;Key Promoter X提醒使用快捷键代替鼠标操作;最后还分享了如何设置背景图片以提升编码心情。
|
4天前
|
缓存 监控 前端开发
【Flutter前端技术开发专栏】Flutter应用的性能调优与测试
【4月更文挑战第30天】本文探讨了Flutter应用的性能调优策略和测试方法。性能调优对提升用户体验、降低能耗和增强稳定性至关重要。优化布局(避免复杂嵌套,使用`const`构造函数)、管理内存、优化动画、实现懒加载和按需加载,以及利用Flutter的性能工具(如DevTools)都是有效的调优手段。性能测试包括基准测试、性能分析、压力测试和电池效率测试。文中还以ListView为例,展示了如何实践这些优化技巧。持续的性能调优是提升Flutter应用质量的关键。
【Flutter前端技术开发专栏】Flutter应用的性能调优与测试
|
4天前
|
前端开发 测试技术 持续交付
【Flutter 前端技术开发专栏】Flutter 中的 UI 测试与自动化测试
【4月更文挑战第30天】本文探讨了 Flutter 应用中UI测试和自动化测试的重要性,包括保障质量、提高效率和增强开发信心。Flutter提供`flutter_test`库进行Widget测试,以及`flutter_driver`进行集成测试。UI测试涵盖界面布局、交互和状态变化的验证,最佳实践建议尽早引入测试、保持用例简洁,并结合手动测试。未来,随着Flutter技术发展,UI测试和自动化测试将更加完善,助力开发高质量应用。
【Flutter 前端技术开发专栏】Flutter 中的 UI 测试与自动化测试
|
4天前
IDEA开发中,类的头位置生成作者时间信息
IDEA开发中,类的头位置生成作者时间信息
|
4天前
|
XML SQL Java
IntelliJ IDEA 插件 MybatisX 在mapper和xml间跳转
IntelliJ IDEA 插件 MybatisX 在mapper和xml间跳转
|
4天前
|
测试技术 持续交付 Swift
【Swift开发专栏】Swift中的测试驱动开发(TDD)
【4月更文挑战第30天】Test-Driven Development (TDD) 是一种软件开发方法,强调先编写测试用例再写代码。通过测试驱动代码、简明设计、重构和持续集成的循环过程,TDD助力构建高质量Swift软件。在Swift中,使用XCTest框架进行TDD实践,包括编写测试用例、实现功能、运行测试和重构。TDD的优势在于提升代码质量、减少调试时间,且与持续集成相结合。然而,学习曲线和确保测试覆盖率是挑战。TDD不仅是技术实践,也是思维方式,随着Swift的发展,其应用将更广泛。
|
5天前
|
人工智能 自然语言处理 Java
用智谱 GLM-4大模型开发自己的IDEA插件
V 哥建议研究大模型先从研究开放 API 开始,作为程序员的你觉对不是什么难事,通过研究 API 的过程中,可以让你更加了解大模型的原理,当然想要更深入的理解,需要对开源大模型的源码进行研究,根据个人的技术能力,量力而行。研究 API 是第一步,目前热招的岗位中,就有基于大模型开放 API 做应用开发的,大家可以在招聘站点上具体看看职位要求。
用智谱 GLM-4大模型开发自己的IDEA插件
|
5天前
|
人工智能 自然语言处理 IDE
如何让阿里云AI001号员工帮我写代码(含IDEA插件使用)
AI 智能时代,将改变所有人的思维方式,学习方式,更注重人的创造力和思考力,如果你懒,你将会被 AI 替代,如果你只想干简单不用动脑的活,你将会被 AI 替代,如果你只会打螺丝,更会被 AI 替代。当下的 AI 人工智能时代,被认为是第四次工业革命的到来,我们更应该看到的是机会,而非跳进焦虑、困惑、悲观的一群人潮中。
|
5天前
|
人工智能 Java 数据库连接
推荐51个 IDEA 常用的插件
在日常撸代码的时候,相信兄弟们在IDEA 中用到不少插件,利用插件,不仅可以提高工具效率,撸起代码来,也格外的娃哈哈……
|
5天前
|
SQL DataWorks Java
DataWorks操作报错合集之在阿里云 DataWorks 中,代码在开发测试阶段能够成功运行,但在提交后失败并报错“不支持https”如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
20 1
DataWorks操作报错合集之在阿里云 DataWorks 中,代码在开发测试阶段能够成功运行,但在提交后失败并报错“不支持https”如何解决

热门文章

最新文章