首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 單元測試 void 方法實戰:Mockito + TestNG 深度解析

     編輯:Java 相關知識

為什麼 void 方法難測試?

Service 層常見的 save*、update* 等方法返回 void,無法像有返回值的方法那樣直接斷言結果。測試重點在於 驗證方法內部邏輯是否正確執行、參數是否符合預期、依賴的外部調用是否被正確觸發。

被測試代碼範例

@Override
public void updateRuleName(Long ruleId, String newRuleName, Long ucId) {
    Assert.notNull(ruleId, "規則ID不能為Null");
    Assert.notNull(newRuleName, "規則名稱不能為Null");
    Assert.notNull(ucId, "操作人的UCID不能為Null");
    
    String cleanNewRuleName = StringUtils.trim(newRuleName);
    if (StringUtils.isBlank(cleanNewRuleName)) {
        throw new IllegalArgumentException("新的規則名稱不能為空");
    }
    
    // 查詢規則對象
    Rule rule = queryRuleById(ruleId);
    if (null == rule) {
        throw new IllegalDataException("沒有查到該規則");
    }
    
    rule.setRuleId(ruleId);
    rule.setRuleName(cleanNewRuleName);
    rule.setUpdateUcid(ucId);
    rule.setUpdateTime(new Date());
    ruleDao.updateSelective(rule);
}

測試代碼實作(Mockito + TestNG)

@Test
public void testUpdateRuleName() {
    Long ruleId = 1L;
    String newRuleName = "newRuleName";
    Long ucId = 123L;
    
    // 準備 Mock 數據
    List<Rule> rules = new ArrayList<>();
    Rule rule = new Rule();
    rule.setRuleStatus((byte) DBValueSetting.RULE_STATUS_TAKE_EFFECT);
    rules.add(rule);
    
    Map<String, Object> params = new HashMap<>();
    params.put("ruleId", ruleId);
    
    // Mock 查詢行為
    Mockito.when(ruleDao.queryRulesByCondition(params)).thenReturn(rules);
    
    // 核心:使用 doAnswer 攔截 void 方法調用,驗證入參
    Mockito.doAnswer(new Answer<Object>() {
        @Override
        public Object answer(InvocationOnMock invocation) {
            // 獲取傳遞給 updateSelective 的 Rule 物件
            Rule rule = (Rule) invocation.getArguments()[0];
            // 斷言驗證關鍵業務欄位
            Assert.assertTrue(rule.getRuleName().equals("newRuleName"));
            return null; // void 方法返回 null
        }
    }).when(ruleDao).updateSelective(Mockito.any(Rule.class));
    
    // 執行被測方法
    ruleService.updateRuleName(ruleId, newRuleName, ucId);
}

核心原理解析

Answer 介面

public interface Answer<T> {
    T answer(InvocationOnMock invocation) throws Throwable;
}

執行流程

  1. 測試代碼執行到 ruleService.updateRuleName(...)
  2. 內部調用 ruleDao.updateSelective(rule)
  3. Mockito 攔截器捕獲該調用,建立靜態代理
  4. 代理執行 Answer.answer() 方法,此時可通過 invocation.getArguments() 獲取實際傳入參數
  5. 在 answer 方法中進行斷言驗證

關鍵點總結

  • doAnswer + when 是測試 void 方法的標準模式
  • InvocationOnMock 提供完整的調用上下文(參數、方法、目標物件)
  • 可驗證:參數值、調用次數、調用順序、異常拋出等
  • 適用於任何無返回值方法的單元測試場景

進階技巧

若需驗證調用次數:

Mockito.verify(ruleDao, Mockito.times(1)).updateSelective(Mockito.any(Rule.class));

若需捕獲多個參數進行複雜驗證:

ArgumentCaptor<Rule> captor = ArgumentCaptor.forClass(Rule.class);
Mockito.verify(ruleDao).updateSelective(captor.capture());
Rule capturedRule = captor.getValue();
// 對 capturedRule 進行任意複雜斷言
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved