测试中使用ConfigurationProperties触发空指针异常问题求助
为啥测试会空指针?怎么修?
嘿,我一眼就看出问题出在哪了——你的测试里ApplicationProperties根本没被正确初始化!
你用@Autowired注入它,但如果测试类没加载Spring上下文(比如没加@SpringBootTest),这个对象就是null;就算加载了上下文,要是配置文件里没填application.bebusiness.end-point-provider,调用getBebusiness()返回的也是null,最后调用getEndPointProvider()的时候自然就触发空指针了。而你换成具体URL后,跳过了对ApplicationProperties的调用,当然就不会碰到这个问题啦。
下面给你几个实用的修复方案,按需选就行:
方案1:用Mockito模拟配置对象(推荐单元测试用)
纯Mockito模拟依赖,不用加载Spring上下文,速度快还能隔离外部影响:
public class ProviderServiceTest { // 把ApplicationProperties和它内部的Bebusiness都mock掉 @Mock private ApplicationProperties applicationProperties; @Mock private ApplicationProperties.Bebusiness bebusinessMock; // 假设你的内部类叫Bebusiness @Mock private RestTemplate restTemplate; @InjectMocks private ProviderServiceImpl providerService; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Test public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() { // 先给mock对象设好返回值 String testUrl = "http://mock-bebusiness-url/api/providers"; when(applicationProperties.getBebusiness()).thenReturn(bebusinessMock); when(bebusinessMock.getEndPointProvider()).thenReturn(testUrl); // 再模拟RestTemplate的返回 String provider = providerInit.buildProviderDTOWithIdFromBebusiness(); ResponseEntity<String> responseEntity = new ResponseEntity<>(provider, HttpStatus.OK); when(restTemplate.getForEntity( anyString(), ArgumentMatchers.any(Class.class) )).thenReturn(responseEntity); // 执行测试 List<ProviderDTO> result = providerService.getAllProviderFromBebusiness(); // 断言结果,还可以顺便验证RestTemplate的调用参数对不对 assertEquals(HttpStatus.OK.value(), responseEntity.getStatusCodeValue()); verify(restTemplate).getForEntity( eq(testUrl + "&page=0&size=" + Integer.MAX_VALUE), eq(String.class) ); } }
方案2:加载Spring上下文用真实配置(适合集成测试)
如果需要验证配置是否正确加载,可以加@SpringBootTest让Spring帮你读配置文件:
@SpringBootTest // 加载Spring上下文,读取application.properties/yaml里的配置 public class ProviderServiceTest { @Autowired private ApplicationProperties applicationProperties; @MockBean // 替换容器里的RestTemplate为mock对象 private RestTemplate restTemplate; @Autowired private ProviderServiceImpl providerService; @Test public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() { // 模拟RestTemplate返回 String provider = providerInit.buildProviderDTOWithIdFromBebusiness(); ResponseEntity<String> responseEntity = new ResponseEntity<>(provider, HttpStatus.OK); when(restTemplate.getForEntity( anyString(), ArgumentMatchers.any(Class.class) )).thenReturn(responseEntity); List<ProviderDTO> result = providerService.getAllProviderFromBebusiness(); assertEquals(HttpStatus.OK.value(), responseEntity.getStatusCodeValue()); } }
这种方式会加载完整Spring上下文,测试速度会慢一点,适合需要验证配置和服务集成的场景。
方案3:手动初始化配置对象
不想用Mockito的话,也可以手动创建并初始化ApplicationProperties:
public class ProviderServiceTest { private ApplicationProperties applicationProperties; @Mock private RestTemplate restTemplate; private ProviderServiceImpl providerService; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Before public void setUp() { // 手动创建配置对象和内部的Bebusiness实例 applicationProperties = new ApplicationProperties(); ApplicationProperties.Bebusiness bebusiness = new ApplicationProperties.Bebusiness(); bebusiness.setEndPointProvider("http://test-url/api/providers"); applicationProperties.setBebusiness(bebusiness); // 直接构造服务对象,把依赖传进去 providerService = new ProviderServiceImpl(applicationProperties, restTemplate); } @Test public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() { // 模拟RestTemplate返回 String provider = providerInit.buildProviderDTOWithIdFromBebusiness(); ResponseEntity<String> responseEntity = new ResponseEntity<>(provider, HttpStatus.OK); when(restTemplate.getForEntity(anyString(), ArgumentMatchers.any(Class.class))) .thenReturn(responseEntity); List<ProviderDTO> result = providerService.getAllProviderFromBebusiness(); assertEquals(HttpStatus.OK.value(), responseEntity.getStatusCodeValue()); } }
最后提醒下
别忘了你的ApplicationProperties内部的Bebusiness类得有对应的getter和setter方法,不然不管是Spring注入还是手动初始化都没法给它赋值哦。
内容的提问来源于stack exchange,提问作者Aymen Kanzari
相关产品推荐
相关产品推荐

