單元測試代碼,自動生成


單測的本質

  • 是要去發現代碼中的問題。

現實中,寫單測環節中可能存在的問題

效率方面

  • 手動代碼低效,特別是代碼重構的時候
  • 有些情況對象稍微大一些,我們就得不停的手動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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM