使用 JUnit 4在 Spring 中进行单元测试的完整步骤

以下是使用 JUnit 4 在 Spring 中进行单元测试的完整步骤,包含配置、核心注解、测试场景及代码示例:


1. 添加依赖

pom.xml 中引入必要的测试依赖(以 Spring 4/5 + JUnit 4 为例):

xml 复制代码
<!-- JUnit 4 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

<!-- Spring Test -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.23</version>
    <scope>test</scope>
</dependency>

<!-- Mock 对象(Mockito) -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.5.1</version>
    <scope>test</scope>
</dependency>

<!-- 数据库测试(H2,用于 DAO 层测试) -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.1.214</version>
    <scope>test</scope>
</dependency>

2. 编写单元测试的核心步骤

步骤 1:测试 Service 层(模拟 DAO 依赖)

使用 SpringJUnit4ClassRunner 加载 Spring 上下文,并通过 @Autowired 注入被测对象。

java 复制代码
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(SpringJUnit4ClassRunner.class) // 使用 Spring 的测试运行器
@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // 加载 Spring 配置文件
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Mock
    private UserDao userDao;

    // 手动注入模拟对象(JUnit 4 需手动初始化)
    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        userService.setUserDao(userDao); // 将模拟对象注入 Service
    }

    @Test
    public void testCreateUser_Success() {
        User user = new User("Alice", "[email protected]");
        
        // 定义模拟行为:当调用 userDao.insertUser() 时返回 true
        when(userDao.insertUser(user)).thenReturn(true);
        
        // 调用被测方法
        boolean result = userService.createUser(user);
        
        // 断言结果
        assertTrue(result);
        verify(userDao, times(1)).insertUser(user);
    }

    @Test
    public void testGetUserById_NotFound() {
        int userId = 999;
        when(userDao.findUserById(userId)).thenReturn(null);
        
        // 断言抛出异常
        try {
            userService.getUserById(userId);
            fail("Expected UserNotFoundException");
        } catch (UserNotFoundException e) {
            assertEquals("User not found", e.getMessage());
        }
    }
}

步骤 2:测试 DAO 层(使用内存数据库 H2)

通过 @RunWith(SpringJUnit4ClassRunner.class) 加载 Spring 上下文,并使用 H2 内存数据库。

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class UserDaoIntegrationTest {

    @Autowired
    private UserDao userDao;

    @Test
    @Transactional // 自动回滚事务,避免污染数据库
    public void testFindUserById() {
        // 插入测试数据
        User user = new User("Bob", "[email protected]");
        userDao.insertUser(user);
        
        // 查询并断言
        User foundUser = userDao.findUserById(user.getId());
        assertNotNull(foundUser);
        assertEquals("Bob", foundUser.getName());
    }
}

步骤 3:测试事务回滚

在 DAO 层测试中,使用 @Transactional 确保测试后数据回滚。

java 复制代码
@Test
@Transactional
public void testCreateUserWithRollback() {
    User user = new User("Charlie", "[email protected]");
    userDao.insertUser(user);
    
    // 测试结束后事务自动回滚,数据库无新增记录
}

步骤 4:测试 REST Controller 层

使用 MockMvc 模拟 HTTP 请求(需手动配置)。

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WebConfig.class}) // 加载 Web 配置类
@WebAppConfiguration // 启用 Web 应用上下文
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void testGetUserById_Success() throws Exception {
        mockMvc.perform(get("/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Alice"));
    }
}

3. 关键注解说明

注解 用途
@RunWith(SpringJUnit4ClassRunner.class) 启用 Spring 的测试运行器,加载应用上下文。
@ContextConfiguration 指定 Spring 配置文件或配置类。
@Autowired 自动注入 Spring 管理的 Bean。
@Transactional 为测试方法启用事务,测试结束后自动回滚。
@Before 在每个测试方法前执行(替代 JUnit 5 的 @BeforeEach)。
@Mock 创建 Mockito 模拟对象(需配合 MockitoAnnotations.initMocks(this) 初始化)。

4. 测试场景与最佳实践

场景 1:隔离依赖(Mocking)
  • 目标:测试 Service 层逻辑时,避免依赖真实数据库。
  • 工具:使用 Mockito 模拟 DAO 层。
场景 2:轻量级集成测试
  • 目标:测试 DAO 层 SQL 是否正确,使用 H2 内存数据库。
  • 工具@Transactional + H2。
场景 3:全栈测试
  • 目标:验证 Controller → Service → DAO 的完整流程。
  • 工具MockMvc + Spring 上下文。
最佳实践
  1. 测试命名 :清晰表达测试目的(如 methodUnderTest_Scenario_ExpectedResult)。
  2. 测试覆盖率:覆盖正常流程、异常分支和边界条件。
  3. 事务管理 :在 DAO 层测试中使用 @Transactional 避免数据残留。
  4. 模拟依赖:使用 Mockito 隔离外部服务(如 HTTP 调用、第三方 API)。

5. 示例:测试异常场景

java 复制代码
@Test(expected = DataAccessException.class)
public void testDatabaseError() {
    User user = new User("David", "[email protected]");
    // 模拟数据库抛出异常
    when(userDao.insertUser(user)).thenThrow(new DataAccessException("Connection failed") {});
    
    userService.createUser(user);
}

6. JUnit 4 与 JUnit 5 的对比

特性 JUnit 4 JUnit 5
注解 @Before, @After @BeforeEach, @AfterEach
参数化测试 需使用 @Parameters + 外部数据源 原生支持 @ParameterizedTest
动态测试 不支持 支持 @DynamicTest
扩展模型 依赖 Spring 的 @RunWith 基于 JPMS 的 @ExtendWith
Java 版本要求 Java 5+ Java 8+

总结

  • JUnit 4 适用场景:维护旧项目、兼容 Java 7 或需要与旧版 Spring 框架集成。
  • JUnit 5 优势:更简洁的语法、更强大的功能(如参数化测试)、更好的模块化支持。

建议

  • 新项目优先使用 JUnit 5。
  • 旧项目可逐步迁移,或通过 junit-vintage-engine 兼容 JUnit 4 和 5 的测试。
相关推荐
C_V_Better17 分钟前
Java Spring Boot 控制器中处理用户数据详解
java·开发语言·spring boot·后端·spring
LUCIAZZZ1 小时前
JVM之虚拟机运行
java·jvm·spring·操作系统·springboot
神秘的t3 小时前
Spring Web MVC————入门(2)
java·spring·mvc
冷心笑看丽美人3 小时前
Spring MVC数据绑定和响应 你了解多少?
java·spring·mvc
蒂法就是我6 小时前
详细说说Spring的IOC机制
java·后端·spring
唐僧洗头爱飘柔95277 小时前
【SSM-SpringMVC(二)】Spring接入Web环境!本篇开始研究SpringMVC的使用!SpringMVC数据响应和获取请求数据
java·spring·文件上传·页面跳转·数据响应·获取请求数据·静态资源访问
-曾牛7 小时前
Spring AI 集成 Mistral AI:构建高效多语言对话助手的实战指南
java·人工智能·后端·spring·microsoft·spring ai
带刺的坐椅9 小时前
SpringBoot2 可以使用 SolonMCP 开发 MCP(江湖救急)
java·spring·ai·solon·mcp
LJianK19 小时前
Spring Boot、Spring MVC 和 Spring 有什么区别
spring boot·spring·mvc
困了又困zZ9 小时前
系统单元测试和项目打包
spring