junit-generator

介绍

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

需求

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

类关系图

主要技术说明

  1. Maven插件开发:见官网:http://maven.apache.org/guides/plugin/guide-java-report-plugin-development.html

  2. XML-DTD 约束文件定义:DTD 的目的是定义 XML 文档的结构,它使用一系列合法的元素来定义文档结构:详解见:

    https://www.cnblogs.com/mengdd/archive/2013/05/30/3107361.html

  3. FreeMarker模板引擎:中文官方参考手册:http://freemarker.foofun.cn/

  4. spi插件机制:见:https://gitee.com/javacoo/xkernel

安装教程

  1. 配置pom

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

    <build><plugins><plugin><groupId>com.javacoo</groupId><artifactId>junit-generator-maven-plugin</artifactId><version>1.1.0-SNAPSHOT</version><configuration><!-- 是否覆盖 --><overwrite>false</overwrite><!-- 是否备份--><backup>true</backup><!-- 配置文件路径 --><configurationFile>src/test/resources/junitGeneratorConfig.xml</configurationFile><!-- 需要执行的上下文ID,多个逗号分隔 --><contexts>testContext,springTestContext</contexts></configuration></plugin></plugins></build>
    
  2. 添加配置文件:junitGeneratorConfig.xml

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

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration PUBLIC "-//javacoo.com//DTD Junit Generator Configuration 1.0//EN" "http://javacoo.com/dtd/junit-generator-config_1_0.dtd" ><!--junit生成配置-->
    <generatorConfiguration><context id="TestContext"><!--junit 模板配置--><template templatePath="/template" templateName="test.ftl" templateHandlerName="spring"></template><!--junit 生成目标类集合--><classList><class className="com.javacoo.junit.generator.api.TestApi"/></classList></context><context id="springTestContext"><!--junit 模板配置--><template templatePath="/template" templateName="test.ftl" templateHandlerName="spring"></template><!--junit 生成目标类集合--><classList><class className="com.javacoo.junit.generator.api.SpringTestApi"/></classList></context><context id="junit5DefaultContext"><!--junit 模板配置--><template templateHandlerName="defaultJUnit5"></template><!--junit 生成目标类集合--><classList><class className="com.javacoo.junit.generator.api.Junit5TestApi"/></classList></context>
    </generatorConfiguration>
    
  3. 生成测试代码:

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

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

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

使用说明

  1. pom.xml 配置说明

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

    参数说明:

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

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

    ​ backup:是否备份->非必填,是指生成测试类前是否备份已有文件,默认为false,不备份(overwrite 为 false 时生效)。
    ​ contexts:需要执行的上下文节点,多个以逗号分隔->非必填,junitGeneratorConfig.xml中context节点id

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

  2. junitGeneratorConfig.xml配置说明

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

    <?xml version="1.0" encoding="UTF-8"?>
    

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

    <!DOCTYPE generatorConfiguration PUBLIC "-//javacoo.com//DTD Junit Generator Configuration 1.0//EN" "http://javacoo.com/dtd/junit-generator-config_1_0.dtd" >
    

    节点说明:

     <!-- generatorConfiguration:配置根节点,必须,整个配置文件唯一,定义生成单元测试相关配置 -->
    <generatorConfiguration>    <!-- context:配置上下文节点,必须,可多个,定义生成所需要的模板信息和目标类信息。属性说明:id,配置上下文的唯一标识,必须,用于在插件配置中指定要执行的上下文节点 --><context id="TestContext">   <!--template: 模板配置信息,非必须,context唯一,默认使用插件自带模板属性说明:templatePath:模板路径信息,非必填,相对测试工程目录的模板路径信息,如果填写,则在指定的模板路径查找模板。templateName:模板名称,非必填,生成单元测试的模板文件名称。templateHandlerName:模板处理器名称,非必填,默认采用插件自带处理器:default插件自带处理器说明:default:基于JUnit4的默认的处理器,用于生成普通类(非Spring项目)的单元测试。spring:基于JUnit4的用于生成Spring工程,相关接口的单元测试。defaultJUnit5:基于JUnit5的默认的处理器,用于生成普通类(非Spring项目)的单元测试。springJUnit5:基于JUnit5的用于生成Spring工程,相关接口的单元测试。mock:基于mockito生成相关接口的单元测试。--><template templatePath="/template" templateName="test.ftl" templateHandlerName="spring"></template><!--classList: 目标类集合节点,必须,context唯一,定义了需要生成单元测试的目标类信息 --><classList><!--class: 目标类定义,必须,定义了需要生成单元测试的目标类信息 属性说明:className:类名称,必须,目标全类名。--><class className="com.javacoo.junit.generator.api.TestApi"/></classList></context>
    </generatorConfiguration>
    
  3. 插件自带模板处理器生成说明:

    • 基于JUnit4->default:基于JUnit4的默认的处理器,生成普通类(非Spring项目)的单元测试,只生成了类或者接口的公共方法的单元测试,如:
     @Testpublic 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 {@Autowiredprivate SpringTestApi springTestApi;@BeforeClasspublic static void setUpClass(){//执行所有测试前的操作}@AfterClasspublic static void tearDownClass(){//执行完所有测试后的操作}@Beforepublic void setUp(){//每次测试前的操作}@Afterpublic void tearDown(){//每次测试后的操作}@Testpublic void testMyTest3(){//TODO: 检查生成的测试代码, 修改给定的方法调用参数和断言子句//准备参数并 调用测试方法String str = "hello";String channelNo = "hello";StoreAreaRequest storeAreaRequest = new StoreAreaRequest(channelNo);List<com.javacoo.junit.generator.model.StoreAreaRequest> storeAreaRequests = new ArrayList<>();storeAreaRequests.add(storeAreaRequest);String str1 = "hello";String channelNo1 = "hello";StoreAreaRequest storeAreaRequest1 = new StoreAreaRequest(channelNo1);Map<java.lang.String, com.javacoo.junit.generator.model.StoreAreaRequest> 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<java.lang.String, com.javacoo.junit.generator.model.FundLoanApproveDetailRequest> map = new HashMap<>();map.put(str, fundLoanApproveDetailRequest);ApprovePreQueryResponse actualResult = springTestApi.myTest3(map);assertNotNull(actualResult);}...
    }
    

最佳实践

  1. 关闭覆盖功能,如:false
  2. 开启备份功能,如:true
  3. 配置需要执行的上下文ID,如:testContext,springTestContext
  4. 在junitGeneratorConfig.xml中定义自己的模块的执行的上下文ID,与其他开发人员隔离

插件开发

  1. 开发步骤

    1. 实现接口:com.javacoo.junit.generator.api.TemplatePlugin,接口定义如下:
     package com.javacoo.junit.generator.api.plugin;import java.util.Map;import com.javacoo.xkernel.spi.Spi;/*** 模板插件* <li>此插件目的是为自定义模板生成规则提供入口,程序会根据插件提供的模板数据渲染指定路径下,指定模板名称的模板,并输出到指定目录</li>* <li>插件机制基于Java SPI机制的扩展,原理及开发步骤见:https://gitee.com/javacoo/xkernel</li>* <li>注意:目前只支持Freemarker模板引擎,开发手册见:http://freemarker.foofun.cn/</li>* @author: duanyong@jccfc.com* @since: 2021/1/4 10:07*/@Spi("default")public interface TemplatePlugin {/*** 根据类对象获取模板数据* <li>此数据用于填充模板</li>* @author duanyong@jccfc.com* @date 2021/1/4 11:09* @param sourceClass:类对象* @return: java.util.Map<java.lang.String,java.lang.Object>*/Map<String, Object> getTemplateData(Class sourceClass);/*** 根据类对象获取输出文件路径* <li>指定测试类文件生成的路径</li>* @author duanyong@jccfc.com* @date 2021/1/4 11:51* @param sourceClass: 类对象* @param outputFilePath: 输出路径* @return: java.lang.String*/String getOutFile(Class sourceClass,String outputFilePath);/*** 获取模板路径* <li>外部模板所在路径</li>* @author duanyong@jccfc.com* @date 2021/1/8 10:57* @return: java.lang.String*/String getTemplatePath();/*** 获取模板名称* <li>模板名称,带后缀</li>* @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;...
    /*** 模板插件接口抽象实现类* <li>定义了插件所需公共方法及流程</li>** @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";/*** 根据类对象获取输出文件路径* <li></li>** @param sourceClass : 类对象* @param outputFilePath: 输出路径* @author duanyong@jccfc.com* @date 2021/1/4 11:51* @return: java.lang.String*/@Overridepublic 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();}/*** 是否需要定义测试类变量* <li></li>* @author duanyong@jccfc.com* @date 2021/1/7 17:18* @return: boolean*/protected boolean needDefineVal(){return true;}/*** 构建模板公共数据Map对象* <li></li>* @author duanyong@jccfc.com* @date 2021/1/7 13:54* @param sourceClass: 目标class对象* @return: java.util.Map<java.lang.String,java.lang.Object>*/protected Map<String, Object> buildCommonDataMap(Class sourceClass) {// 定义模板数据Map<String, Object> data = new HashMap<>(6);//组装基础数据到模板数据Map对象populateBaseData(sourceClass, data);//组装方法数据到模板数据Map对象populateMethodMetaData(sourceClass, data);return data;}...
    }

    DefaultJUnit4TemplatePlugin:

    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模板插件默认实现* <li></li>** @author: duanyong@jccfc.com* @since: 2021/1/4 11:18*/
    public class DefaultJUnit4TemplatePlugin extends AbstractTemplatePlugin {/*** 根据类对象获取模板数据* <li></li>** @param sourceClass :类对象* @author duanyong@jccfc.com* @date 2021/1/4 11:09* @return: java.util.Map<java.lang.String, java.lang.Object>*/@Override
    public Map<String, Object> getTemplateData(Class sourceClass) {Map<String, Object> data = buildCommonDataMap(sourceClass);return data;}/*** 获取模板路径* <li></li>** @author duanyong@jccfc.com* @date 2021/1/8 10:57* @return: java.lang.String*/@Overridepublic String getTemplatePath() {return BASE_TEMPLATE_PACKAGE+ JUnitVersionEnum.JUNIT4.getCode();}/*** 获取模板名称* <li></li>** @author duanyong@jccfc.com* @date 2021/1/5 11:41* @return: java.lang.String 模板名称*/@Overridepublic String getTemplateName() {return TemplateTypeEnum.TEMPLATE_TYPE_ENUM_DEFAULT.getValue();}
    }
    1. 编写模板文件,如:基于JUnit4的普通类测试模板文件:DefaultTemplate.ftl

      package ${basePackage};import org.junit.*;
      import static org.junit.Assert.*;<#list importClasses as importClass>
      import ${importClass};
      </#list>/**
      * ${className}的测试类
      *
      * @author ${author!''}
      * @date ${date}
      */
      public class ${className}Test {@BeforeClasspublic static void setUpClass(){//执行所有测试前的操作}@AfterClasspublic static void tearDownClass(){//执行完所有测试后的操作}@Beforepublic void setUp(){//每次测试前的操作}@Afterpublic void tearDown(){//每次测试后的操作}<#list methods as method>@Testpublic void test${method.methodName?cap_first}(){${method.methodBody!''}}
      </#list>}
      
    2. 注册接口:在项目resources目录下创建:META-INF/ext目录,并创建一个文本文件:名称为接口的“全限定名”,内容格式为:实现名=实现类的全限定名,如。文件名为:com.javacoo.junit.generator.api.TemplatePlugin。内容如下:

      myTemplateHander=com.xxx.plugin.MyJUnit4TemplateHanderPlugin
      

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

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

future

  • ​ 基于JUnit5的单元测试
    参数化单元测试
  •     支持Mock
    默认mockito实现
    

项目地址:https://gitee.com/javacoo/junit-generator

一些信息

路漫漫其修远兮,吾将上下而求索
码云:https://gitee.com/javacoo
QQ群:164863067
作者/微信:javacoo
邮箱:xihuady@126.com

Junit 单元测试生成工具Maven插件相关推荐

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

    junit-generator 介绍 一个基于JUnit,Freemarker,Mockito,Maven等技术实现的单元测试类脚手架生成工具Maven插件. 需求 我们在测试驱动开发过程中,总会写一 ...

  2. mybatis代码自动生成工具之maven插件mybatis-generator-maven-plugin(mybatis逆向工程)

    mybatis自动代码生成工具maven插件可以帮助我们轻松的生成pojo.dao.mapper.xml文件,相比于mybatis-generator等其他方式方便许多,话不多说,上代码 先看一下整体 ...

  3. 开发工具 Maven、Git、IDEA插件

    文章目录 一.Maven - 1.企业级架构 - 2.Maven 项目构建工具 - - 1.概述 - - 2.为何需要maven? - 3.四大特征 - - 1.仓库 repository - - 2 ...

  4. maven 插件之 AutoConfig 工具使用笔记

    AutoConfig 是一款 maven 插件,主要用于 Maven 项目打包使用.在我们的工作中,会将自己写的代码打成 jar 包或者 war 包发布到各种环境上.一般地,不用的环境所使用的数据库. ...

  5. maven jacoco_使用JaCoCo Maven插件为单元和集成测试创建代码覆盖率报告

    maven jacoco 当我开始使用Java 7时,我立即注意到Cobertura Maven插件不支持它 . 这对我来说是个大问题,因为我每天都使用代码覆盖率报告. 我做了一些研究,发现了JaCo ...

  6. 使用JaCoCo Maven插件为单元和集成测试创建代码覆盖率报告

    当我开始使用Java 7时,我立即注意到Cobertura Maven插件不支持它 . 这对我来说是个大问题,因为我每天都使用代码覆盖率报告. 我做了一些研究,发现了JaCoCo代码覆盖库 . 看起来 ...

  7. JAVAWEB(三)Java与数据库(JUnit、JUL、Maven、图书管理系统)

    使用JUnit进行单元测试 !!!!!! 前排提醒:我们要将pom.xml中的junit版本改成4.12及以上的! !!!!!! 首先一问:我们为什么需要单元测试? 随着我们的项目逐渐变大,比如我们之 ...

  8. Java技术:项目构建工具Maven最佳替代者gradle介绍

    Maven作为一款非常流行的项目构建工具.基本上是每个Java程序员必备的工具,当然Maven有一些地方不足之处: 1. Maven的配置文件是XML格式的,如果你的项目工程依赖的包很多,那么XML文 ...

  9. dojo还有人用吗_我的Dojo中有一个Mojo(如何编写Maven插件)

    dojo还有人用吗 我一直忙于在工作中使用Maven的腋窝. 对于很多开发人员,我会听到:"那又怎样." 区别在于,我通常在无法直接访问Internet的环境中工作. 因此,当我说 ...

最新文章

  1. VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION
  2. Smartform下載PDF
  3. my footprint :走过的路
  4. c语言编写图形登录窗口,「分享」C语言如何编写图形界面
  5. mysql sqrt_详解MySQL中的SQRT函数的使用方法
  6. c语言扫掠数组,科学网—COMSOL 个人笔记 - 刘铨鸿的博文
  7. axure rp10安装教程,axurerp10安装步骤
  8. 凯利讯分享ECL电路与TTL电路的使用注意事项
  9. 初级程序员应该怎么选电脑?来自从业5年维修工程师的建议
  10. stream流的常用方法
  11. 什么叫少儿机器人编程
  12. Ubuntu安装运行YOLOV3 解决opencv报错 No package ‘opencv‘ found
  13. C++如何实现系统语言切换功能,MessageBox的确认/取消按钮语言显示如何跟程序一致
  14. (一)微信公众号环境搭建与开发接入
  15. Jenkins构建(14):Jenkin实现自动化更新服务(一)
  16. 做PPT必备的大数据分析网站,好看又免费的报表工具
  17. 一文搞定 Linux 设备树
  18. 【转】苹果iPhone常见名词术语
  19. Python编程之整数移位
  20. go依赖注入--google开源库wire

热门文章

  1. processflow利用drawio实现多人协作画流程图功能
  2. TensorFlow实现模型评估
  3. 基于layui + springboot +shiro+mybatisPlus仓库管理系统
  4. NETBIOS名 和 Host名的不同
  5. 用linux下的C语言编程万年历,shell编程万年历月历和对应c语言程序.docx
  6. 程序员锁死服务器毁掉600万游戏项目?当事人回应
  7. java中mvc模式是什么_什么是MVC模式 MVC模式中三者之间关系
  8. java Boolean 比较
  9. 基于Java编程俄罗斯方块的重现
  10. Alta PCI-1553B的BC端操作——认识Alta卡