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
相关产品推荐
相关产品推荐

