zoukankan      html  css  js  c++  java
  • 单元测试代码,自动生成

    单测的本质

    • 是要去发现代码中的问题。

    现实中,写单测环节中可能存在的问题

    效率方面

    • 手动代码低效,特别是代码重构的时候
    • 有些情况对象稍微大一些,我们就得不停的手动set,耗费大量时间

    质量方面

    • 应付:为了单测而单测
    • 单测不严谨,等于没有

    对标

    该项目对标JUnitGenerator V2.0

    优劣势

    • 优势
      • 较JUnitGenerator V2.0显著提升研发效率。生成代码的调用,和猜测的验证

    本插件的特色

    • 自动的生成方法的调用,帮你自动的创建变量、方法的的参数,已随机值的方式生成,无需手动(set)
    • 针对调用完成的方法,根据是否有返回值以及变量自动生成Assert的代码,选择性的使用

    目前支持的功能

    • 针对于单纯的javaBean的code生成,适用于util类、DDD中的领域层等等
    • mock类
      • 生成Jmockit风格的单测代码

    未来要支持的功能

    • 更好的去猜测如何设置调用的case、需要多少case以及如何Assert
    • 支持更多风格的单测生成,比如Spock等

    插件的思路

    • 方向1:将人工的单测经验,通过java代码来实现(目前的方案)
    • 方向2:通过优质的单测例子,使用机器学习手段,不断地训练,生产单测(可能是未来的方案)

    过程中的问题思考

    如何指望代码不严谨的人,单测写的严禁?

    矛盾:代码写的不好,单测的代码写的也可能会不好,所以需要通过代码按时,足够充分的Assert帮助提升质量

    如何区分单测的代码风格?比如普通的Junit or Jmockit

    • 标识
      • 文件名结尾,一般的以Service、ServiceImpl、Application等结尾的一般都是需要mock的,故使用mock风格,但是具体哪种mock的组件,后面会提供配置的页面

    如何写调用的case?

    • 调用参数生成
      • 基本类型 随机生成
      • 引用类型 随机生成
        • 支持代码随机过程可视化,这样可以随意修改想要的值
    • 方法调用
      • 如何做多case调用,去猜测真正的调用case
      • 私有方法处理
        • 通过反射将access变成true,调用
      • 常规方法处理

    如何写断言

    • 根据返回值类型
      • void
        • 方法没有入参 无需断言
        • 方法有入参 验证当前对象属性的改变
      • boolean 验证返回值
      • 其他基本类型 验证返回值
      • 引用类型,验证非空,验证返回值属性

    想法

    • 业务驱动单元测试,业务语言转换成测试用例,测试用例转换成代码
    • 将基本的api开放,通过集成,使用者根据自己的情况编写模板
    • 如何做成一款产品,卖出去

    插件获取

    - 源代码生成插件(推荐):
        - 下载代码:git clone git地址(下面有)
        - 进入根目录:cd unit-test
        - 执行命令:./generatePlugin.sh
        - 得到结果:
        ```
        请确保已经安装了Gradle
        
        Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
        Use '--warning-mode all' to show the individual deprecation warnings.
        See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warnings
        
        BUILD SUCCESSFUL in 4s
        10 actionable tasks: 1 executed, 9 up-to-date
        cp: build/distributions/ is a directory (not copied).
        插件生成成功:unit.test-0.0.2.zip
    
        ```
    
    - 直接下载(可能会有版本问题,导致不兼容,用下面的):https://plugins.jetbrains.com/plugin/download?pluginId=org.xiaogang.unit.test&version=0.0.2 
    

    插件安装

    • idea本地安装就可以

    如何使用

    • 添加pom:配套的pom:主要用来配合生成单测的代码
    <dependency>
      <groupId>com.alibaba.cro</groupId>
      <artifactId>unit-test-api</artifactId>
      <version>1.0.0-SNAPSHOT</version>
    </dependency> 
    
    
    • 在要测试的代码中,右键,选择【generate...】,然后选择【Auto Generation Test Code】,然后会弹出一个对话框,选择要生成单测的方法
      业务代码:
        public Exam paramStr(String str, Exam e) {
            if (viewAnswer == null) {
                return null;
            }
            return this;
        }
    
        public boolean canViewAnswer() {
            if (viewAnswer == null) {
                return false;
            }
            return viewAnswer == 1;
        }
    

    生成后的单测如下:

       @Test
        public void testParamStr() { 
            // Initialize params of the method;
            String str = ObjectInit.random(String.class);
            Exam e = ObjectInit.random(Exam.class);
            Exam invokeResult = exam.paramStr(str, e);
    
            // Write the Assert code
            //Assert.assertEquals(expected, actual);
            //Assert.assertEquals(expected, invokeResult);
        }
    
     
        @Test
        public void testCanViewAnswer() { 
            Boolean invokeResult = exam.canViewAnswer();
    
            // Write the Assert code
            //Assert.assertEquals(expected, actual);
            //Assert.assertEquals(expected, invokeResult);
        }
    
    
    • 针对于需要mock的代码,比如xxService
      业务代码:对第三方的api调用是需要mock的
    public boolean canGoWorld(String batchId) {
            SimpleResultDTO<SimpleBatchInfoDTO> resultDTO = inspectHsfService.searchBatch(batchId);
            if (!resultDTO.isSuccess() || resultDTO.getData() == null) {
                log.error("batchId [" + batchId + "] searchBatch fail,detail:" + JSON
                    .toJSONString(resultDTO));
                throw new RuntimeException("调用RCP异常");
            }
            if (StringUtils.equals(BatchStatusEnum.NO_COMPLETE.name(), resultDTO.getData().getStatus())) {
                return true;
            }
            return false;
        }
    

    单测代码: 这里会把需要mock的数据,猜测出来,然后开发可以自由的去调整,目前是基于Jmockit来实现的

        @Test
        public void testCanGoWorld() { 
            // Initialize params of the method;
            String batchId = ObjectInit.random(String.class);
            new Expectations() {{
                //inspectHsfService.searchBatch(batchId);
                //result = ObjectInit.random(SimpleResultDTO<SimpleBatchInfoDTO>.class);// mock的返回值,这里可以手动的修改,ObjectInit是工具类,随机初始化bean
            }};
            Boolean invokeResult = taskService.canGoWorld(batchId);// 如果有调用的返回值,下面的Assert也会提示要验证这个值
    
            //new Verifications() {{
            //}};
            // Write the Assert code
            //Assert.assertEquals(expected, actual);
            //Assert.assertEquals(expected, invokeResult);
        }
    

    源码

    • gitlab: git@gitlab.alibaba-inc.com:shishang.fxg/unit-test.git
    • github: git@github.com:xiaogangfan/unit-test.git
  • 相关阅读:
    linux下mysql命令大全
    Linux 随记
    Linux服务器部署系列之八—Sendmail篇
    Linux服务器部署系列之六—远程管理篇
    ubuntu简易教程(如何使用noi linux)
    51nod1254 最大子段和 V2 DP
    [HAOI2007]理想的正方形 st表 || 单调队列
    [LG1886]滑动窗口 单调队列
    [51nod1791] 合法括号子段 DP
    [51nod1503]猪和回文 DP
  • 原文地址:https://www.cnblogs.com/xiaogangfan/p/11435453.html
Copyright © 2011-2022 走看看