跳转至

Unittest

單元測試 Unit testing

目的: 自動化測試程式的正確性

所謂的單元測試 ,就是一次只測試一個功能點 ,一個單元 Unit 可以是一個 method ,或是一個API

其他軟體測試:

  • 整合測試 (Integration testing)
  • 端對端測試 (end-to-end testing)

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

單元測試實作

創建一個 Calculator 的 class

package com.example.demo.UnitTest;

public class Calculator {
    public int add(int x ,int y){
        return x - y;
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        int result = calculator.add(1,2);
        System.out.println(result);
    }
}

快速創建一個 Test

Test 的 class

assertEquals 判斷說 (預期 , calculator.add() 方法的結果) 是否相等

package com.example.demo.UnitTest;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class CalculatorTest {
    @Test
    public void test(){
        Calculator calculator = new Calculator();
        int result = calculator.add(1,2);
        assertEquals(3,result); // assert 斷言 Equals 相等
    }
}

顯示畫面

成功

失敗

單元測試的特性

  • 可以被自動化運行
  • 各個單元測試互相獨立 ,彼此之間不能有依賴關係
  • 測試的結果是穩定的 , 不受外部服務影響

單元測試的注意事項

  • 測試的程式要放在test資料夾裡面 (方便管理程式)
    • src (source)
      • main 放我們要寫的code
        • java 放 java code
        • resources 放 Spring Boot 設定檔
      • test 放測試用的code
        • java 放測試用 java code
        • resources 放測試用 Spring Boot 設定檔
  • 測試的 class 以 原 class 的名字加上 Test 作為結尾 來命名
  • 測試的 class 的 package 跟原 class 的 package 保持一致

都會放在com.example.demo 底下

真的需要單元測試嗎?

  • 單元測試的數量不是越多越好
    • 單元測試也是程式 ,程式越多就表示維護成本越高
    • 單元測試的重要程度 = 影響使用者的程度 使用頻率越高的功能越需要單元測試
  • 要加多少單元測試?
    • 金融業: 能測多細就多細 ,錢很重要
    • 軟體業:
      • 使用 MockMvc 測式各個API 功能是否運作正常
      • 重要的 Service 層可以加一些專屬的單元測試
      • Dao 層比較少測 ,但如果有複雜的 sql 語法 , 可以額外添加測試

如何寫出好的單元測試

  • 在寫單元測試時 ,要從使用者的角度去思考
    • 經驗: 不要在時做完就直接寫單元測試 ,可以休息一下再寫
  • 一定要記得測試 Error Case (錯誤驗證)
    • ex: 在測試getById 方法時 ,可以測試當 id 不存在時該怎麼處理? 當id 長度超過限制時該如何處理? Error Handling!!!
  • 善用 IntelliJ Run Test With Converage ,查看單元測試覆蓋的範圍

可以列出單元測試測出來的到底是哪幾行的程式 ,計算出單元測試的覆蓋率

More RunDebug >Run Tests in ‘’ with Converage

  • 不要為了單純提升測試覆蓋率而寫單元測試 , 而是要思考是否有哪些場景沒考慮到
  • 不要倍數字迷惑 ,並不是一定要 100% 覆蓋率才是完美 ,只要確保重要的功能有被測試到即可
  • 如果測試中使用的@SpyBean 過多 ,表示功能切分的不夠好
    • 盡量從功能面去區分 Service ,ex: PrdouctService ,OrderService ,UserService
    • 和外部服務有關的API call ,獨立成一個 Service
      • ex: 和 YouTube 有關的 API call 就統一放在 YouTubeService 裡面 在這個 Service 專門去處理和 YouTube 服務之間的 http 溝通
      • 靠經驗多累積

測試驅動開發 TDD

全稱 Test-Driven Development 簡稱TDD

  • 貫徹 先寫測試 ,再寫開發 的精神
  • 大致上可以分為五個步驟
    1. 選擇一個功能 ,先寫單元測試
    2. 單元測試失敗 (紅燈)
    3. 實作程式
    4. 單元測試成功 (綠燈)
    5. 持續重構程式