java生成单元测试工具_junit-generator Junit 单元测试生成工具Maven插件

junit-generator是一个基于JUnit、Freemarker、Mockito和Maven的单元测试生成工具,旨在减少开发者编写模板式测试代码的工作量。通过在pom.xml配置并运行插件,可以自动生成JUnit4的普通类或Spring项目的测试代码。配置文件junitGeneratorConfig.xml用于定制生成规则,提供覆盖、备份和执行上下文选择等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

junit-generator

介绍

一个基于JUnit,Freemarker,Mockito,Maven等技术实现的单元测试类脚手架生成工具Maven插件。

需求

我们在测试驱动开发过程中,总会写一大堆与业务无关的模板式的代码,为了减少开发者写单元测试的工作量,需要一个单元测试类脚手架代码的生成工具。

类关系图

009c6d3603fa

类关系图

主要技术说明

安装教程

配置pom

在测试工程的pom.xml文件中添加如下配置:

com.javacoo

junit-generator-maven-plugin

1.1.0-SNAPSHOT

false

true

src/test/resources/junitGeneratorConfig.xml

testContext,springTestContext

添加配置文件:junitGeneratorConfig.xml

在项目resources目录下添加junitGeneratorConfig.xml配置文件:如

生成测试代码:

在IDE工具栏查看安装好插件,点击运行,如:

009c6d3603fa

输入图片说明

或者执行命令:mvn com.javacoo:junit-generator-maven-plugin:1.1.0-SNAPSHOT:generate

生成结果:默认在测试工程 src/test/java 目录生成测试类包名文件夹及测试类,如:

009c6d3603fa

输入图片说明

使用说明

pom.xml 配置说明

junit-generator-maven-plugin按照标准Maven插件配置即可。

参数说明:

​ skip:是否跳过生成>非必填,是指是否跳过生成测试类文件,默认为false,不跳过,即生成。

​ overwrite:是否覆盖->非必填,是指是否覆盖已有的测试类文件,默认为false,不覆盖,即合并。

​ backup:是否备份->非必填,是指生成测试类前是否备份已有文件,默认为false,不备份(overwrite 为 false 时生效)。

​ contexts:需要执行的上下文节点,多个以逗号分隔->非必填,junitGeneratorConfig.xml中context节点id

​ configurationFile:配置文件路径->必填,相对测试项目根目录

junitGeneratorConfig.xml配置说明

第一行为标准XML文件定义:

第二行为junit-generator-maven-plugin特有DTD文件约束说明:

节点说明:

插件自带模板处理器生成说明:

基于JUnit4->default:基于JUnit4的默认的处理器,生成普通类(非Spring项目)的单元测试,只生成了类或者接口的公共方法的单元测试,如:

@Test

public void testAddAndGet(){

//TODO: 检查生成的测试代码, 修改给定的方法调用参数 并 断言子句

//准备参数并 调用测试方法

long l = 0L;

AtomicLong atomicLong = new AtomicLong(l);

long l1 = 0L;

long actualResult = atomicLong.addAndGet(l1);

assertEquals("addAndGet方法", 0L, actualResult);

}

基于JUnit4->spring:基于JUnit4的用于生成Spring工程,相关接口的单元测试,只生成了类或者接口的公共方法的单元测试,如:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = {

"classpath*:/spring/spring-mvc.xml"

})

public class SpringTestApiTest {

@Autowired

private SpringTestApi springTestApi;

@BeforeClass

public static void setUpClass(){

//执行所有测试前的操作

}

@AfterClass

public static void tearDownClass(){

//执行完所有测试后的操作

}

@Before

public void setUp(){

//每次测试前的操作

}

@After

public void tearDown(){

//每次测试后的操作

}

@Test

public void testMyTest3(){

//TODO: 检查生成的测试代码, 修改给定的方法调用参数和断言子句

//准备参数并 调用测试方法

String str = "hello";

String channelNo = "hello";

StoreAreaRequest storeAreaRequest = new StoreAreaRequest(channelNo);

List storeAreaRequests = new ArrayList<>();

storeAreaRequests.add(storeAreaRequest);

String str1 = "hello";

String channelNo1 = "hello";

StoreAreaRequest storeAreaRequest1 = new StoreAreaRequest(channelNo1);

Map storeAreaRequestMap = new HashMap<>();

storeAreaRequestMap.put(str1, storeAreaRequest1);

String isFaceCheck = "hello";

BigDecimal approveAmt = BigDecimal.ZERO;

FundLoanApproveRequest fundLoanApproveRequest = new FundLoanApproveRequest(approveAmt);

FundLoanApproveDetailRequest fundLoanApproveDetailRequest = new FundLoanApproveDetailRequest(storeAreaRequests, storeAreaRequestMap, isFaceCheck, fundLoanApproveRequest);

Map map = new HashMap<>();

map.put(str, fundLoanApproveDetailRequest);

ApprovePreQueryResponse actualResult = springTestApi.myTest3(map);

assertNotNull(actualResult);

}

...

}

最佳实践

关闭覆盖功能,如:false

开启备份功能,如:true

配置需要执行的上下文ID,如:testContext,springTestContext

在junitGeneratorConfig.xml中定义自己的模块的执行的上下文ID,与其他开发人员隔离

...

插件开发

开发步骤

实现接口:com.javacoo.junit.generator.api.TemplatePlugin,接口定义如下:

package com.javacoo.junit.generator.api.plugin;

import java.util.Map;

import com.javacoo.xkernel.spi.Spi;

/**

* 模板插件

*

此插件目的是为自定义模板生成规则提供入口,程序会根据插件提供的模板数据渲染指定路径下,指定模板名称的模板,并输出到指定目录

*

插件机制基于Java SPI机制的扩展,原理及开发步骤见:https://gitee.com/javacoo/xkernel

*

注意:目前只支持Freemarker模板引擎,开发手册见:http://freemarker.foofun.cn/

* @author: duanyong@jccfc.com

* @since: 2021/1/4 10:07

*/

@Spi("default")

public interface TemplatePlugin {

/**

* 根据类对象获取模板数据

*

此数据用于填充模板

* @author duanyong@jccfc.com

* @date 2021/1/4 11:09

* @param sourceClass:类对象

* @return: java.util.Map

*/

Map getTemplateData(Class sourceClass);

/**

* 根据类对象获取输出文件路径

*

指定测试类文件生成的路径

* @author duanyong@jccfc.com

* @date 2021/1/4 11:51

* @param sourceClass: 类对象

* @param outputFilePath: 输出路径

* @return: java.lang.String

*/

String getOutFile(Class sourceClass,String outputFilePath);

/**

* 获取模板路径

*

外部模板所在路径

* @author duanyong@jccfc.com

* @date 2021/1/8 10:57

* @return: java.lang.String

*/

String getTemplatePath();

/**

* 获取模板名称

*

模板名称,带后缀

* @author duanyong@jccfc.com

* @date 2021/1/5 11:41

* @return: java.lang.String 模板名称

*/

String getTemplateName();

}

基于JUnit默认实现类代码片段如下:

AbstractTemplatePlugin

package com.javacoo.junit.generator.internal.plugin.junit4;

...

/**

* 模板插件接口抽象实现类

*

定义了插件所需公共方法及流程

*

* @author: duanyong@jccfc.com

* @since: 2021/1/5 14:27

*/

public abstract class AbstractTemplatePlugin implements TemplatePlugin {

/**默认模板路径*/

protected static final String BASE_TEMPLATE_PACKAGE = "/templates/";

/**日期格式*/

private final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

/**返回变量名*/

private final String RESULT_VAL_NAME = "actualResult";

/**

* 根据类对象获取输出文件路径

*

*

* @param sourceClass : 类对象

* @param outputFilePath: 输出路径

* @author duanyong@jccfc.com

* @date 2021/1/4 11:51

* @return: java.lang.String

*/

@Override

public String getOutFile(Class sourceClass,String outputFilePath) {

Package sourcePackage = sourceClass.getPackage();

//包路径

StringBuilder packagePath = new StringBuilder().append(outputFilePath).append("/").append(sourcePackage.getName().replace(".","/")).append("/");

//生成文件夹

File filePath = new File(packagePath.toString());

if (!filePath.exists()){

filePath.mkdirs();

}

//文件名称

String fileName = new StringBuilder().append(sourceClass.getSimpleName().substring(0, 1).toUpperCase()).append(sourceClass.getSimpleName().substring(1)).toString();

//输出文件路径

StringBuilder outFile = packagePath.append(fileName).append("Test.java");

return outFile.toString();

}

/**

* 是否需要定义测试类变量

*

* @author duanyong@jccfc.com

* @date 2021/1/7 17:18

* @return: boolean

*/

protected boolean needDefineVal(){

return true;

}

/**

* 构建模板公共数据Map对象

*

* @author duanyong@jccfc.com

* @date 2021/1/7 13:54

* @param sourceClass: 目标class对象

* @return: java.util.Map

*/

protected Map buildCommonDataMap(Class sourceClass) {

// 定义模板数据

Map data = new HashMap<>(6);

//组装基础数据到模板数据Map对象

populateBaseData(sourceClass, data);

//组装方法数据到模板数据Map对象

populateMethodMetaData(sourceClass, data);

return data;

}

...

}

DefaultJUnit4TemplatePlugin:

```java

package com.javacoo.junit.generator.internal.plugin.junit4;

import java.util.Map;

import com.javacoo.junit.generator.enmus.JUnitVersionEnum;

import com.javacoo.junit.generator.enmus.TemplateTypeEnum;

/**

* JUnit4模板插件默认实现

*

*

* @author: duanyong@jccfc.com

* @since: 2021/1/4 11:18

*/

public class DefaultJUnit4TemplatePlugin extends AbstractTemplatePlugin {

/**

* 根据类对象获取模板数据

*

*

* @param sourceClass :类对象

* @author duanyong@jccfc.com

* @date 2021/1/4 11:09

* @return: java.util.Map

*/

@Override

public Map getTemplateData(Class sourceClass) {

Map data = buildCommonDataMap(sourceClass);

return data;

}

/**

* 获取模板路径

*

*

* @author duanyong@jccfc.com

* @date 2021/1/8 10:57

* @return: java.lang.String

*/

@Override

public String getTemplatePath() {

return BASE_TEMPLATE_PACKAGE+ JUnitVersionEnum.JUNIT4.getCode();

}

/**

* 获取模板名称

*

*

* @author duanyong@jccfc.com

* @date 2021/1/5 11:41

* @return: java.lang.String 模板名称

*/

@Override

public String getTemplateName() {

return TemplateTypeEnum.TEMPLATE_TYPE_ENUM_DEFAULT.getValue();

}

}

```

编写模板文件,如:基于JUnit4的普通类测试模板文件:DefaultTemplate.ftl

package ${basePackage};

import org.junit.*;

import static org.junit.Assert.*;

import ${importClass};

#list>

/**

* ${className}的测试类

*

* @author ${author!''}

* @date ${date}

*/

public class ${className}Test {

@BeforeClass

public static void setUpClass(){

//执行所有测试前的操作

}

@AfterClass

public static void tearDownClass(){

//执行完所有测试后的操作

}

@Before

public void setUp(){

//每次测试前的操作

}

@After

public void tearDown(){

//每次测试后的操作

}

@Test

public void test${method.methodName?cap_first}(){

${method.methodBody!''}

}

#list>

}

注册接口:在项目resources目录下创建:META-INF/ext目录,并创建一个文本文件:名称为接口的“全限定名”,内容格式为:实现名=实现类的全限定名,如。文件名为:com.javacoo.junit.generator.api.TemplatePlugin。内容如下:

myTemplateHander=com.xxx.plugin.MyJUnit4TemplateHanderPlugin

格式为:处理器名称=处理器实现类全路径类名

009c6d3603fa

3.png

使用:在junitGeneratorConfig.xml配置文件的template节点,配置属性 templateHandlerName="myTemplateHander"

future

​ 基于JUnit5的单元测试

参数化单元测试

支持Mock

默认mockito实现

一些信息

路漫漫其修远兮,吾将上下而求索

码云:https://gitee.com/javacoo

QQ群:164863067

作者/微信:javacoo

邮箱:xihuady@126.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值