單元測試 Unit Test

簡介

測試通常分為三個階段:
1. Unit Test:確保程式的執行符合預期
2. 系統整合測試(SIT,System integration testing):確保系統各模組的交互沒有問題
3. 使用者驗收測試(UAT,User Acceptance Testing):確保符合用戶的需求與期望

單元測試是品質管理的一部份
一般來說交付的程式必須包含 Unit Test 程式碼

原則
– 最小測試單位
– 一個測試案例只測一個 Function
– 一個輸入對應一個輸出,不會有判斷
– 不同 input 就是不同 test case
– 一個 Function 若有多種邏輯判斷,則分別對應一個測試
– 無相依性
– 允許斷網
– 不會與其他測試案例交互
– 有修改就要重測

3A法則:Arrange → Act → Assert

mock
– 模擬外部回傳資料
– 關注點分離

使用

  1. 使用套件 spring-boot-starter-test 已包含 junit 與 mockito
  2. 建立測試 Class
    滑鼠右鍵 → Generate… → Test…
    test 資料夾下會建立與 main 資料夾對應的目錄與類別
  3. 註解
    1. @ExtendWith(MockitoExtension.class) 註解在測試類別上
    2. @Mock 註解在假資料上
    3. @InjectMocks 註解在要注入假資料的物件
    4. @Test 註解在方法上
  4. 撰寫測試方法
    1. 設定假資料的操作行為
      when().thenReturn();
      when().thenThrow();
      
    2. 如果參數不重要,會搭配以下方法,傳入符合型別的資料即可
      any();
      anyString();
      anyByte();
      anyBoolean();
      anyChar();
      anyCollection();
      anyDouble();
      anyFloat();
      anyInt();
      anyIterable();
      anyList();
      anyLong();
      anyMap();
      anySet();
      anyShort();
      
    3. 檢核運行結果 org.junit.jupiter.api.Assertions
      assertTrue(result);
      assertFalse(result);
      
      assertNull(result);
      assertNotNull(result);
      
      assertSame(a, b); // a == b
      assertNotSame(a, b); // a != b
      
      assertEquals(a, b);
      assertNotEquals(a, b);
      
      assertArrayEquals(a, b);
      assertIterableEquals(a, b);
      
      assertThrows(Exception.class, () -> {}); // Class.isInstance()
      assertThrowsExactly(Exception.class, () -> {});
      assertDoesNotThrow(() -> {});
      
      assertTimeout(Duration.ofSeconds(5), () -> {}); // 等待全部執行完畢
      assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {}); // 一超時馬上失敗
      
  5. 執行程式
    1. Run method
    2. Run class 會執行類別中所有被 @Test 註解的方法
    3. Run All Tests with Coverage 檢查專案的測試覆蓋率
  6. 範例
    @ExtendWith(MockitoExtension.class)
    public class UserServiceTest {
        @Mock
        private UserRepository userRepository;
        @InjectMocks
        private UserService userService = new UserService();
    
        @Test
        public void test_getUserById_userExists() {
            //arrange
            Long userId = 1L;
            User user = new User(userId, "Tom");
            when(userRepository.findById(userId)).thenReturn(Optional.of(user));
            //act
            User result = userService.getUserById(userId);
            //assert
            assertEquals(user, result);
        }
    
        @Test
        public void test_getUserById_userNotFound() {
            //arrange
            Long userId = 2L;
            when(userRepository.findById(userId)).thenReturn(Optional.empty());
            //act
            EntityNotFoundException exception = assertThrows(EntityNotFoundException.class, () -> {
                userService.getUserById(userId);
            });
            //assert
            assertEquals("User not found with ID: 2", exception.getMessage());
        }
    }
    
    

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *