You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mockito when().thenReturn未被覆盖导致单元测试失败求助

问题场景与代码

我编写了如下单元测试代码:

public class TdsConfigTest {

@InjectMocks
TdsConfig tdsConfig;

@Mock
Configuration config;

@BeforeMethod
public void setup() {
    MockitoAnnotations.initMocks(this);
    Mockito.when(config.getInt(SettlementConstants.TDS_LIMIT, SettlementConstants.DEFAULT_TDS_LIMIT)).thenReturn(10000);
    Mockito.when(config.getInt(SettlementConstants.NEW_TDS_LIMIT, 10000)).thenReturn(0);
    Mockito.when(config.getInt(SettlementConstants.TDS_PERCENT_VALUE, SettlementConstants.DEFAULT_TDS_PERCENT_VALUE)).thenReturn(30);
    Mockito.when(config.getInt(SettlementConstants.NEW_TDS_PERCENT_VALUE, 30)).thenReturn(20);
}

@Test
public void returnsValidPercentBeforeCutoff() {
    mockFutureTdsCutoff();
    assertEquals(30, tdsConfig.getTdsPercentage());
    assertNotEquals(20, tdsConfig.getTdsPercentage());
}

@Test
public void returnsValidTdsLimitBeforeCutoff() {
    mockFutureTdsCutoff();
    assertEquals(10000, tdsConfig.getTdsLimit());
    assertNotEquals(0, tdsConfig.getTdsLimit());
    Mockito.reset();
}

@Test
public void returnsValidPercentAfterCutoff() {
    mockPastTdsCutoff();
    assertEquals(20, tdsConfig.getTdsPercentage());
    assertNotEquals( 30, tdsConfig.getTdsPercentage());
}

@Test
public void returnsValidLimitAfterCutoff() {
    mockPastTdsCutoff();
    assertEquals(tdsConfig.getTdsLimit(), 0);
    assertNotEquals(tdsConfig.getTdsLimit(), 10000);
}


private Instant getFutureInstant() {
    return Instant.now().plus(1, ChronoUnit.DAYS);
}

private Instant getPastInstant() {
   return Instant.now().minus(1, ChronoUnit.DAYS);
}

private void mockFutureTdsCutoff() {
    Instant futureMs = getFutureInstant();
    Mockito.when(config.getLong(Mockito.anyString(), Mockito.anyLong())).thenReturn(futureMs.toEpochMilli());
}

private void mockPastTdsCutoff() {
    Mockito.when(config.getLong(Mockito.anyString(), Mockito.anyLong())).thenReturn(getPastInstant().toEpochMilli());
}
}

前两个测试方法本应使用模拟的未来Instant,后两个测试方法使用模拟的过去Instant,但实际前两个测试失败。调试发现测试执行顺序为倒序(最后一个测试先执行),导致mockFutureTdsCutoff()的模拟被先执行的mockPastTdsCutoff()覆盖,请问该如何解决这个问题?


解决方案

1. 统一在测试后重置Mock对象

目前只有第二个测试手动调用了Mockito.reset(),其他测试没有做清理,导致Mock的状态在测试间残留。可以新增@AfterMethod方法,统一重置Mock的config对象,确保每个测试的Mock状态都是独立的:

@AfterMethod
public void tearDown() {
    Mockito.reset(config);
}

这样不管测试执行顺序如何,每个测试结束后都会清空Mock的所有模拟配置,避免互相干扰。

2. 替换宽泛的匹配器为精确匹配

你当前的mockFutureTdsCutoff()和mockPastTdsCutoff()使用了Mockito.anyString()匹配所有getLong调用,这会导致后续的Mock覆盖之前的配置。应该针对具体的配置Key做精确匹配,假设截止日期的配置Key是SettlementConstants.TDS_CUTOFF_DATE,修改后的Mock方法如下:

private void mockFutureTdsCutoff() {
    Instant futureMs = getFutureInstant();
    Mockito.when(config.getLong(SettlementConstants.TDS_CUTOFF_DATE, Mockito.anyLong()))
           .thenReturn(futureMs.toEpochMilli());
}

private void mockPastTdsCutoff() {
    Mockito.when(config.getLong(SettlementConstants.TDS_CUTOFF_DATE, Mockito.anyLong()))
           .thenReturn(getPastInstant().toEpochMilli());
}

精确匹配只会针对目标配置Key生效,不会覆盖其他Mock逻辑,同时也让测试逻辑更清晰。

3. 强制指定测试执行顺序(应急方案,不推荐)

如果需要临时解决问题,可以通过TestNG的priority参数指定测试执行顺序,让前两个测试先执行:

@Test(priority = 1)
public void returnsValidPercentBeforeCutoff() {
    // ... 原有逻辑
}

@Test(priority = 1)
public void returnsValidTdsLimitBeforeCutoff() {
    // ... 原有逻辑
}

@Test(priority = 2)
public void returnsValidPercentAfterCutoff() {
    // ... 原有逻辑
}

@Test(priority = 2)
public void returnsValidLimitAfterCutoff() {
    // ... 原有逻辑
}

注意:单元测试的核心原则是独立性,依赖执行顺序会让测试变得脆弱,所以这个方法仅作为临时应急,不推荐长期使用。


内容的提问来源于stack exchange,提问作者Sriram R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:38:21