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

简介: 很多公司对分支单测覆盖率会有一定的要求,比如 单测覆盖率要达到 60% 或者 80%才可以发布。有时候工期相对紧张,就优先开发功能,测试功能,然后再去补单元测试。![在这里插入图片描述](https://img-blog.csdnimg.cn/e9e8ea7d35ca4830bce7929774471207.jpg)但是编写单元测试又比较浪费时间,有没有能够很大程度上自动化生成单元测试的插件,自己简单改改即可呢?自己尝试在 Idea 插件库里搜索相关插件并去尝试使用,发现 `TestMe` 还可以。后面和其他同学交流,谎伴 同学推荐他一直在用的 `Squaretest`,我试用

一、背景

很多公司对分支单测覆盖率会有一定的要求,比如 单测覆盖率要达到 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 {

    @Resource
    private 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 {

    @Mock
    private UserDAO mockUserDAO;

    @InjectMocks
    private UserManager userManagerUnderTest;

    @Test
    public void testSomeThing() {
        // Setup
        final 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 test
        final List<UserDO> result = userManagerUnderTest.someThing(param);

        // Verify the results
    }

    @Test
    public void testSomeThing_UserDAOReturnsNoItems() {
        // Setup
        final Param param = new Param();
        param.setUserIds(Arrays.asList("value"));
        param.setOthers("others");

        when(mockUserDAO.findByIds(Arrays.asList("value"))).thenReturn(Collections.emptyList());

        // Run the test
        final List<UserDO> result = userManagerUnderTest.someThing(param);

        // Verify the results
        assertEquals(Collections.emptyList(), result);
    }
}

官方演示1:选择性生成测试代码
在这里插入图片描述官方演示2:选择需要 mock 的属性
在这里插入图片描述

官方示例3:在单测里写 test 即可选择需要测试的方法自动生成测试代码
在这里插入图片描述

2.2.2 定制化

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

public class UserManager {

    @Setter
    private 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 {

    @Resource
    private 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 {
    @Mock
    UserDAO userDAO;
    @InjectMocks
    UserManager userManager;

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

    @Test
    public 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};
#end

import 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)

    @Before
    public void setUp() {
    }
#end
#foreach($method in $TESTED_CLASS.methods)
#if($TestSubjectUtils.shouldBeTested($method))

    @Test
    public 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 {
    @Mock
    UserDAO userDAO;

    @InjectMocks
    UserManager userManager;

    @Before
    public void setUp() {
    }

    @Test
    public 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》

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

四、总结

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

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


创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。
在这里插入图片描述
相关文章
|
10天前
|
IDE Java 关系型数据库
Java 初学者学习路线(含代码示例)
本教程为Java初学者设计,涵盖基础语法、面向对象、集合、异常处理、文件操作、多线程、JDBC、Servlet及MyBatis等内容,每阶段配核心代码示例,强调动手实践,助你循序渐进掌握Java编程。
96 3
|
13天前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
93 3
|
13天前
|
Java
怎么用Java 代码示例来展示继承的实现
本文通过Java代码示例展示继承机制:Animal为父类,Cat和Dog继承其属性与方法,并实现构造函数调用、方法重写与特有功能扩展,体现代码复用与多态特性。
54 4
|
14天前
|
Java
java入门代码示例
本文介绍Java入门基础,包含Hello World、变量类型、条件判断、循环及方法定义等核心语法示例,帮助初学者快速掌握Java编程基本结构与逻辑。
204 0
|
27天前
|
Java API 开发工具
【Azure Developer】Java代码实现获取Azure 资源的指标数据却报错 "invalid time interval input"
在使用 Java 调用虚拟机 API 获取指标数据时,因本地时区设置非 UTC,导致时间格式解析错误。解决方法是在代码中手动指定时区为 UTC,使用 `ZoneOffset.ofHours(0)` 并结合 `withOffsetSameInstant` 方法进行时区转换,从而避免因时区差异引发的时间格式问题。
139 3
|
1月前
|
缓存 Java 开发者
Java 开发者必看!ArrayList 和 LinkedList 的性能厮杀:选错一次,代码慢成蜗牛
本文深入解析了 Java 中 ArrayList 和 LinkedList 的性能差异,揭示了它们在不同操作下的表现。通过对比随机访问、插入、删除等操作的效率,指出 ArrayList 在多数场景下更高效,而 LinkedList 仅在特定情况下表现优异。文章强调选择合适容器对程序性能的重要性,并提供了实用的选择法则。
108 3
|
2月前
|
人工智能 监控 安全
智慧工地解决方案,java智慧工地程序代码
智慧工地系统融合物联网、AI、大数据等技术,实现对施工现场“人、机、料、法、环”的全面智能监控与管理,提升安全、效率与决策水平。
|
2月前
|
算法 IDE Java
Java 项目实战之实际代码实现与测试调试全过程详解
本文详细讲解了Java项目的实战开发流程,涵盖项目创建、代码实现(如计算器与汉诺塔问题)、单元测试(使用JUnit)及调试技巧(如断点调试与异常排查),帮助开发者掌握从编码到测试调试的完整技能,提升Java开发实战能力。
272 0
|
3月前
|
安全 Java 测试技术
Java 项目实战中现代技术栈下代码实现与测试调试的完整流程
本文介绍基于Java 17和Spring技术栈的现代化项目开发实践。项目采用Gradle构建工具,实现模块化DDD分层架构,结合Spring WebFlux开发响应式API,并应用Record、Sealed Class等新特性。测试策略涵盖JUnit单元测试和Testcontainers集成测试,通过JFR和OpenTelemetry实现性能监控。部署阶段采用Docker容器化和Kubernetes编排,同时展示异步处理和反应式编程的性能优化。整套方案体现了现代Java开发的最佳实践,包括代码实现、测试调试
143 0

热门文章

最新文章