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

Spring Boot测试@PostConstruct的init()方法遇空指针异常求助

解决Spring Boot单元测试中@Autowired注入空指针的问题

首先,你遇到的空指针异常核心原因是:你的测试类用错了注解。@SpringBootApplication是用来标记Spring Boot应用的主启动类的,不是测试类的注解——Spring测试框架不会用这个注解来初始化测试上下文,所以@Autowired的userIdService根本没有被实例化,自然会抛出NullPointerException。

下面给你两种可行的解决方案,分别对应集成测试和纯单元测试场景:

方案一:集成测试(加载完整Spring上下文)

如果你想测试真实的Spring环境下UserIdService的初始化行为(包括真实加载CSV文件),可以用@SpringBootTest注解标记测试类。这个注解会启动完整的Spring上下文,自动注入你的UserIdService实例,而且@PostConstruct修饰的init()方法会在Bean初始化时自动执行,不需要你手动调用。

修改后的测试类代码如下:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class UserIdServiceTest {

    @Autowired
    private UserIdService userIdService;

    @Test
    void testInit_ValidRecordsLoaded() {
        // 验证CSV文件中的有效记录是否被正确加载
        // 假设你的测试CSV里有一个有效号码"1234567890"
        String validNumber = PhoneNumberHelper.convertPhoneNumber("1234567890");
        assertTrue(userIdService.contains(validNumber));
        
        // 验证加载的记录数符合预期(比如CSV里有3条有效记录)
        assertEquals(3, userIdService.get(validNumber).size());
    }

    @Test
    void testAdd_NewRecord() {
        UserId newRecord = new UserId("Alice", "9876543210", "personal");
        assertTrue(userIdService.add(newRecord));
        assertTrue(userIdService.contains("9876543210"));
    }

    // 其他testGet、testContains方法可以类似编写
}

方案二:纯单元测试(不依赖Spring上下文)

如果想更高效地测试init()方法的逻辑(比如验证有效/无效号码的处理、CSV解析逻辑),可以用Mockito模拟ResourceLoader依赖,不需要启动整个Spring容器,测试速度更快。

Spring Boot的spring-boot-starter-test已经包含了Mockito,直接用即可:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class UserIdServiceUnitTest {

    @Mock
    private ResourceLoader resourceLoader; // 模拟ResourceLoader依赖

    @InjectMocks
    private UserIdService userIdService; // 自动注入模拟的依赖到UserIdService

    @Test
    void testInit_LoadsValidAndSkipsInvalidRecords() throws Exception {
        // 构造测试用的CSV内容:一行有效号码,一行无效号码
        String testCsvContent = "1234567890,work,John Doe\ninvalid-phone,home,Jane Doe";
        InputStream testInputStream = new ByteArrayInputStream(testCsvContent.getBytes());

        // 模拟ResourceLoader返回的资源
        Resource mockResource = mock(Resource.class);
        when(mockResource.getInputStream()).thenReturn(testInputStream);
        when(resourceLoader.getResource("classpath:data/userid-data.csv")).thenReturn(mockResource);

        // 调用init方法
        userIdService.init();

        // 验证有效号码被正确加载
        String convertedValidNumber = PhoneNumberHelper.convertPhoneNumber("1234567890");
        assertTrue(userIdService.contains(convertedValidNumber));
        assertEquals(1, userIdService.get(convertedValidNumber).size());

        // 验证无效号码被跳过
        assertFalse(userIdService.contains(PhoneNumberHelper.INVALID_INPUT));
    }

    @Test
    void testAdd_DuplicateContextReturnsFalse() {
        UserId firstRecord = new UserId("John", "1234567890", "work");
        userIdService.add(firstRecord);
        
        // 尝试添加相同号码和上下文的记录
        UserId duplicateRecord = new UserId("Jane", "1234567890", "work");
        assertFalse(userIdService.add(duplicateRecord));
    }
}

关键注意点

  1. 不要在测试类上使用@SpringBootApplication,测试类的正确注解是@SpringBootTest(集成测试)或@ExtendWith(MockitoExtension.class)(单元测试)。
  2. 集成测试中,@PostConstruct的init()方法会自动执行,不需要手动调用userIdService.init(),否则会重复执行初始化逻辑。
  3. 单元测试中,通过Mockito模拟外部依赖(比如ResourceLoader),可以完全控制输入,专注测试业务逻辑本身。

内容的提问来源于stack exchange,提问作者maciekgt3

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:18:17