为调用动态值函数的rollDays编写单元测试时遇空指针异常求助
问题分析与解决方案
1. NullPointerException 根本原因
你遇到的NPE是因为错误尝试存根被测试类(CalculateDateService)的方法:
tested是通过@InjectMocks创建的真实实例,并非Mockito mock对象,所以when(tested.Closed(...))和when(tested.NonSettle(...))会直接调用真实方法。- 在调用
tested.Closed(initDate, dateService.marID)时,dateService.today()还未被mock,返回null,导致NonSettle方法中LocalDate.of(dateService.today().getYear(), ...)因null.getYear()抛出NPE。
2. 修正测试的核心原则
- 仅mock依赖的
dateService的所有被调用方法(Holiday、Disruption、today()),不要试图存根被测试类的方法。 - 确保
dateService.today()返回有效年份,避免NonSettle方法出错。 - 针对
rollDays的动态循环,分场景覆盖不同情况(普通工作日、遇到非结算日/节假日)。
3. 修正后的测试代码
@Mock private DateService dateService; @InjectMocks private CalculateDateService tested; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); // 确保mock实例初始化 } @Test public void testRollDays_SkipHolidaysAndNonSettle() { LocalDate initDate = LocalDate.of(2022, 12, 22); String marId = DateService.marID; // Mock 当前日期为2022年,确保平安夜(12.24)、新年前夜(12.31)被识别为非结算日 when(dateService.today()).thenReturn(initDate); // Mock 圣诞节(12.25)、节礼日(12.26)为节假日 when(dateService.Holiday(LocalDate.of(2022,12,25), marId)).thenReturn(true); when(dateService.Holiday(LocalDate.of(2022,12,26), marId)).thenReturn(true); // 所有日期无业务中断 when(dateService.Disruption(any(LocalDate.class), eq(marId))).thenReturn(false); // 滚动逻辑:从12.22开始滚4个有效工作日,跳过12.24(非结算)、12.25-26(节假日),最终到12.29 LocalDate rolledDate = tested.rollDays(4, initDate, marId); LocalDate correctRolledDate = LocalDate.of(2022, 12, 29); assertEquals(correctRolledDate, rolledDate); } // 测试无特殊日期的普通滚动场景 @Test public void testRollDays_RegularDays() { LocalDate initDate = LocalDate.of(2023, 1, 2); String marId = DateService.marID; when(dateService.today()).thenReturn(initDate); when(dateService.Holiday(any(LocalDate.class), eq(marId))).thenReturn(false); when(dateService.Disruption(any(LocalDate.class), eq(marId))).thenReturn(false); // 滚3天:1.2→1.3→1.4→1.5 LocalDate rolledDate = tested.rollDays(3, initDate, marId); LocalDate correctRolledDate = LocalDate.of(2023, 1, 5); assertEquals(correctRolledDate, rolledDate); }
4. 动态for循环的测试技巧
- 明确循环终止逻辑:循环会持续执行,直到当前日期不处于关闭状态且已滚完指定天数(
Closed(date, mar)为false,同时numOfDays-- >0为false)。 - 多场景覆盖:
- 普通工作日场景:验证滚动天数与输入一致。
- 含关闭日期场景:验证自动跳过节假日/非结算日,最终滚动的有效天数符合预期。
- 负数天数场景:测试向前滚动的反向逻辑。
- 参数化测试优化:如果需要覆盖更多日期组合,可使用JUnit 5的
@ParameterizedTest批量测试不同输入和预期输出,减少重复代码。
内容的提问来源于stack exchange,提问作者w97802
相关产品推荐
相关产品推荐

