如何为私有变量编写JUnit测试用例?解决effectiveEndDate空指针异常
问题解决:NullPointerException 排查与私有变量的JUnit测试方案
一、effectiveEndDate 空指针异常的原因与解决
原因分析
测试环境中未正确加载Spring上下文或未注入@Value对应的配置属性,导致effectiveEndDate为null,调用DateTimeUtil.convertStringToDate(null)时触发空指针异常。
解决方案
1. 业务代码层面增加空判断(防御性编程)
在mapMarkupModelToEntity方法中先判空,避免空值传入转换工具类:
public CommodityMarkup mapMarkupModelToEntity(CommodityMarkupView commodityMarkupView) { CommodityMarkup commodityMarkup = new CommodityMarkup(); if (effectiveEndDate != null) { commodityMarkup.setEffectiveEndDate(DateTimeUtil.convertStringToDate(effectiveEndDate)); } // 其他属性映射逻辑 return commodityMarkup; }
2. 测试环境中正确注入属性
方式一:加载Spring上下文并指定测试属性
使用@SpringBootTest启动Spring上下文,通过@TestPropertySource直接指定测试用的属性值:
@SpringBootTest @TestPropertySource(properties = {"default.effective.endDate=12/31/9999"}) public class CustomerCommodityMarkupMapServiceTest { @Autowired private CustomerCommodityMarkupMapService customerCommodityMarkupMapService; @Test void testMapMarkupModelToEntity() { CommodityMarkupView view = new CommodityMarkupView(); // 模拟view的属性 CommodityMarkup actualMarkup = customerCommodityMarkupMapService.mapMarkupModelToEntity(view); CommodityMarkup expectedMarkup = new CommodityMarkup(); expectedMarkup.setEffectiveEndDate(DateTimeUtil.convertStringToDate("12/31/9999")); assertEquals(expectedMarkup, actualMarkup); } }
方式二:单元测试中手动设置私有变量(不加载Spring上下文)
通过反射直接修改私有变量的值,适合轻量单元测试:
public class CustomerCommodityMarkupMapServiceTest { private CustomerCommodityMarkupMapService service = new CustomerCommodityMarkupMapService(); @BeforeEach void setUp() throws NoSuchFieldException, IllegalAccessException { // 获取私有字段并设置可访问 Field endDateField = CustomerCommodityMarkupMapService.class.getDeclaredField("effectiveEndDate"); endDateField.setAccessible(true); // 设置测试用的属性值 endDateField.set(service, "12/31/9999"); } @Test void testMapMarkupModelToEntity() { CommodityMarkupView view = new CommodityMarkupView(); CommodityMarkup actualMarkup = service.mapMarkupModelToEntity(view); CommodityMarkup expectedMarkup = new CommodityMarkup(); expectedMarkup.setEffectiveEndDate(DateTimeUtil.convertStringToDate("12/31/9999")); assertEquals(expectedMarkup, actualMarkup); } }
二、私有变量的JUnit测试方案
通常不建议直接测试私有变量,而是通过测试公共方法的行为来验证私有变量的作用。如果需要为不同私有变量值编写测试,推荐以下方案:
1. 重构为构造函数注入(推荐)
将私有变量改为构造函数注入,测试时可直接传入不同值,符合依赖注入原则,测试更简洁:
@Component public class CustomerCommodityMarkupMapService { private final String effectiveEndDate; // 通过构造函数注入属性 public CustomerCommodityMarkupMapService(@Value("${default.effective.endDate}") String effectiveEndDate) { this.effectiveEndDate = effectiveEndDate; } // 业务方法... }
测试代码:
@Test void testMapMarkupModelToEntityWithCustomEndDate() { String testEndDate = "01/01/2025"; CustomerCommodityMarkupMapService service = new CustomerCommodityMarkupMapService(testEndDate); CommodityMarkup actualMarkup = service.mapMarkupModelToEntity(new CommodityMarkupView()); assertEquals(DateTimeUtil.convertStringToDate(testEndDate), actualMarkup.getEffectiveEndDate()); }
2. 使用Spring工具类ReflectionTestUtils
如果是Spring组件,加载上下文后用ReflectionTestUtils快速设置私有变量:
@SpringBootTest public class CustomerCommodityMarkupMapServiceTest { @Autowired private CustomerCommodityMarkupMapService service; @Test void testWithDifferentEndDate() { // 设置私有变量值 ReflectionTestUtils.setField(service, "effectiveEndDate", "06/30/2024"); CommodityMarkup actualMarkup = service.mapMarkupModelToEntity(new CommodityMarkupView()); assertEquals(DateTimeUtil.convertStringToDate("06/30/2024"), actualMarkup.getEffectiveEndDate()); } }
3. 反射直接操作私有字段
如前文单元测试中的setUp方法所示,通过Java反射API直接修改私有变量,适合非Spring环境的单元测试。
内容的提问来源于stack exchange,提问作者HARIHARAN P
相关产品推荐
相关产品推荐

