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

为调用动态值函数的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:01:41