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

Spring Boot中Spring Data JDBC的CrudRepository无数据库Mock问题

解决Spring Boot + Spring Data JDBC纯单元测试Mock仓库的问题

首先,你提到要做完全脱离数据库的纯单元测试,那最直接且高效的方式是不用启动Spring容器——纯单元测试的核心就是测试单一组件(比如你的UserService),依赖用Mockito模拟即可,不需要Spring的上下文支持。

方案一:真正的纯单元测试(推荐)

这种方式完全不需要任何Spring配置,也不会加载数据库相关的任何组件,测试速度极快。

修改后的测试代码

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
    // 用@Mock让Mockito自动创建UserRepository的模拟实例
    @Mock
    private UserRepository userRepository;
    
    private UserService userService;

    @BeforeEach
    void setUp() {
        // 通过构造函数注入模拟的仓库,实例化服务类
        userService = new UserService(userRepository);
    }

    @Test
    public void testGetUserById() {
        // 预设模拟仓库的行为:当调用findById(1L)时返回指定用户
        User mockUser = new User(1L, "testUser");
        when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));
        
        // 调用服务层方法
        User result = userService.getUserById(1L);
        
        // 验证结果符合预期
        assertEquals("testUser", result.getUsername());
        // 验证模拟仓库的方法确实被调用了
        verify(userRepository).findById(1L);
    }
}

为什么这个方案可行?

  • 用@ExtendWith(MockitoExtension.class)替代Spring的扩展,让Mockito负责创建模拟对象,不需要Spring参与。
  • 直接实例化UserService,手动注入模拟的UserRepository,完全脱离Spring容器和数据库配置。
  • 没有任何Spring相关的启动开销,测试执行速度非常快。

方案二:如果必须使用Spring上下文(比如服务层有其他Spring管理的依赖)

如果你因为某些原因需要在Spring上下文里测试(比如服务层依赖了其他Spring bean),可以调整配置避免加载数据库相关组件,同时确保模拟的仓库能被Spring识别。

步骤1:调整测试配置

在测试的application.properties里保留数据源相关的自动配置排除,但移除JdbcRepositoriesAutoConfiguration的排除(因为我们需要Spring识别仓库接口,即使是模拟的):

spring.autoconfigure.exclude= \
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration

步骤2:创建测试专用配置类

只加载测试需要的组件,避免扫描整个应用(防止其他依赖数据库的组件导致启动失败):

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;

@TestConfiguration
public class UserServiceTestConfig {
    // 手动创建UserService的bean,注入模拟的仓库
    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserService(userRepository);
    }
}

步骤3:修改测试类

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;

// 只加载测试专用配置类,不加载整个应用
@SpringBootTest(classes = UserServiceTestConfig.class)
public class UserServiceTest {
    @Autowired
    private UserService userService;
    
    // 用@MockBean向Spring上下文注册模拟的UserRepository
    @MockBean
    private UserRepository userRepository;

    @Test
    public void testGetUserById() {
        User mockUser = new User(1L, "testUser");
        when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));
        
        User result = userService.getUserById(1L);
        
        assertEquals("testUser", result.getUsername());
        verify(userRepository).findById(1L);
    }
}

你原来的错误原因分析

你之前的测试用了@SpringBootTest但没有指定classes参数,导致Spring加载了整个应用上下文。如果你的应用里有其他组件(比如控制器、其他服务)依赖了数据源或数据库相关bean,而你又排除了自动配置,Spring就会找不到这些bean而报错。另外,@Transactional注解在纯单元测试里是多余的——没有数据库事务需要管理,反而会增加不必要的Spring配置开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 17:42:33