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

Spring Boot Test JUnit空指针异常求助:单元测试新手的服务测试问题

解决Spring Service单元测试的JUnit空指针异常

兄弟,我刚入门写单元测试的时候也踩过一模一样的坑!你的空指针大概率是因为Service里的TopicRepository没被正确初始化——毕竟Spring的@Autowired是靠容器注入的,但普通JUnit测试默认不会启动Spring容器,或者你没做好依赖Mock。结合你的TopicService代码,给你两种最常用的解决思路:

方法一:用Mockito做纯单元测试(推荐)

这种方式不需要启动Spring容器,只Mock掉TopicRepository,专注测试Service的业务逻辑,速度快还能隔离依赖。

步骤&示例代码:

  1. 给测试类加@ExtendWith(MockitoExtension.class)(JUnit 5写法,JUnit 4用@RunWith(MockitoJUnitRunner.class))
  2. 用@InjectMocks标注你的TopicService实例,Mockito会自动把Mock的依赖注入进去
  3. 用@Mock标注TopicRepository,创建一个Mock对象代替真实的Repository
package com.praxis.topics.service;

import com.praxis.topics.exception.EntityNotFoundException;
import com.praxis.topics.model.Topic;
import com.praxis.topics.model.enums.Status;
import com.praxis.topics.repository.TopicRepository;
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 java.time.LocalDateTime;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class TopicServiceTest {

    @Mock
    private TopicRepository topicRepository; // Mock掉Repository,避免依赖真实数据库

    @InjectMocks
    private TopicService topicService; // 自动把Mock的Repository注入到Service里

    @Test
    void whenGetExistingTopic_thenReturnTopic() {
        // 准备测试用的Mock数据
        Topic mockTopic = new Topic();
        mockTopic.setId(1L);
        mockTopic.setTitle("JUnit测试入门");
        mockTopic.setStatus(Status.ACTIVE);
        mockTopic.setCreatedAt(LocalDateTime.now());

        // 定义Mock行为:当调用findById(1L)时返回我们准备好的mockTopic
        when(topicRepository.findById(1L)).thenReturn(Optional.of(mockTopic));

        // 调用Service的方法
        Topic result = topicService.getTopicById(1L);

        // 验证结果是否符合预期
        assertNotNull(result);
        assertEquals("JUnit测试入门", result.getTitle());
        // 还能验证Repository的方法是否被正确调用过
        verify(topicRepository, times(1)).findById(1L);
    }

    @Test
    void whenGetNonExistingTopic_thenThrowEntityNotFoundException() {
        // 定义Mock行为:找不到对应ID时返回空Optional
        when(topicRepository.findById(999L)).thenReturn(Optional.empty());

        // 断言会抛出预期的异常
        assertThrows(EntityNotFoundException.class, () -> topicService.getTopicById(999L));
    }
}

为什么能解决NPE?

@Mock会创建一个TopicRepository的Mock实例,@InjectMocks会自动把这个Mock注入到TopicService的对应字段里,这样Service里的topicRepository就不再是null,调用方法自然不会空指针了。

方法二:用Spring Boot Test做集成测试

如果你想测试Service和真实Repository的交互(比如和数据库的CRUD操作),可以用Spring Boot Test启动完整的Spring容器:

步骤&示例代码:

  1. 测试类加@SpringBootTest,启动Spring容器
  2. 用@Autowired注入TopicService和TopicRepository(如果不想用真实Repository,也可以用@MockBean替换)
package com.praxis.topics.service;

import com.praxis.topics.model.Topic;
import com.praxis.topics.model.enums.Status;
import com.praxis.topics.repository.TopicRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class TopicServiceIntegrationTest {

    @Autowired
    private TopicService topicService;

    @Autowired
    private TopicRepository topicRepository;

    @Test
    void whenCreateTopic_thenTopicIsSaved() {
        // 准备测试数据
        Topic newTopic = new Topic();
        newTopic.setTitle("集成测试示例");
        newTopic.setStatus(Status.DRAFT);
        newTopic.setCreatedAt(LocalDateTime.now());

        // 调用Service的创建方法
        Topic savedTopic = topicService.createTopic(newTopic);

        // 验证保存结果
        assertNotNull(savedTopic.getId());
        assertEquals("集成测试示例", savedTopic.getTitle());
        // 验证数据库中确实存在这条数据
        assertTrue(topicRepository.existsById(savedTopic.getId()));
    }
}

注意点:

这种方式启动Spring容器的速度较慢,适合做集成测试;纯单元测试还是推荐第一种Mockito的方式,保证测试的独立性和执行效率。

额外提醒的坑:

  • 不要忘记加Mockito的扩展类/运行器,否则@Mock和@InjectMocks不会生效,还是会NPE
  • 单元测试尽量Mock外部依赖,不要直接连真实数据库,避免测试受环境影响

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:34:52