首页 > 基础资料 博客日记

springboot~3.x项目中使用集成测试

2025-08-26 17:30:03基础资料围观18

这篇文章介绍了springboot~3.x项目中使用集成测试,分享给大家做个参考,收藏Java资料网收获更多编程知识

在 Spring Boot 3.x 中为控制器编写集成测试,主要是通过 @SpringBootTest 注解加载完整的应用上下文,并利用 MockMvcTestRestTemplate 来模拟 HTTP 请求并验证响应。下面我将为你提供一个清晰的指南和代码示例。

两种测试

在Spring Boot项目中,测试通常分为单元测试和集成测试。以下是区分这两种测试的一些指导原则:

单元测试

  • 定义:单元测试主要用于测试代码中的单个“单元”,通常是一个方法或类。它们的目标是验证特定功能的正确性。
  • 特征
    • 独立性:单元测试不依赖于外部系统(如数据库、网络等)。
    • 快速执行:通常执行时间很短。
    • 使用Mock:通常会使用Mock对象来替代依赖项,以便只测试目标单元。
  • 示例
    @SpringBootTest
    public class UserServiceTest {
        
        @Mock
        private UserRepository userRepository;
    
        @InjectMocks
        private UserService userService;
    
        @Test
        public void testGetUserById() {
            // Arrange
            User user = new User(1, "John Doe");
            when(userRepository.findById(1)).thenReturn(Optional.of(user));
            
            // Act
            User result = userService.getUserById(1);
            
            // Assert
            assertEquals("John Doe", result.getName());
        }
    }
    

集成测试

  • 定义:集成测试用于测试多个组件之间的交互,通常是测试整个应用程序或其部分的行为。
  • 特征
    • 依赖性:集成测试通常会启动Spring上下文,并可能连接到数据库或其他外部服务。
    • 较慢执行:由于涉及多个组件,执行时间通常较长。
    • 真实环境:测试在接近真实环境的条件下运行。
  • 示例
    @SpringBootTest
    @AutoConfigureMockMvc
    public class UserControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void testGetUser() throws Exception {
            mockMvc.perform(get("/users/1"))
                   .andExpect(status().isOk())
                   .andExpect(jsonPath("$.name").value("John Doe"));
        }
    }
    

如何区分

  1. 测试目标

    • 单元测试:关注单个类或方法。
    • 集成测试:关注多个组件的协作。
  2. 使用的工具

    • 单元测试:Mockito、JUnit等。
    • 集成测试:Spring Test、MockMvc等。
  3. 项目结构

    • 可以在src/test/java目录中创建不同的包,例如unitintegration,分别存放单元测试和集成测试。
  4. 命名约定

    • 可以在文件名中添加前缀或后缀,例如UserServiceTest(单元测试)和UserControllerIntegrationTest(集成测试)。

🧪 Spring Boot 3.x 控制器集成测试指南

📝 1. 添加测试依赖

确保你的 pom.xml 中包含 Spring Boot Test starter 依赖,它通常已经包含了 JUnit Jupiter、Mockito、AssertJ 等测试所需的库。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

🧱 2. 创建控制器

假设你有一个简单的 REST 控制器 ExampleController

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class ExampleController {

    @GetMapping("/greet")
    public String greet(@RequestParam(required = false, defaultValue = "World") String name) {
        return "Hello, " + name + "!";
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        // 假设这是一个创建用户并返回创建后信息的服务
        return userService.save(user); // userService 需要通过依赖注入
    }
}

🧪 3. 编写集成测试类

创建一个集成测试类,使用 @SpringBootTest@AutoConfigureMockMvc 来配置测试环境。

3.1 使用 MockMvc (模拟 MVC 环境)

这种方法不会启动真正的服务器,而是模拟 Servlet 环境,测试速度较快。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

// 使用 @SpringBootTest 加载完整的应用程序上下文
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) // WebEnvironment.MOCK 是默认值,可省略
@AutoConfigureMockMvc // 自动配置 MockMvc
public class ExampleControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper; // Jackson 库的 ObjectMapper,用于对象与 JSON 转换

    @Test
    public void testGreetEndpoint() throws Exception {
        mockMvc.perform(get("/api/greet") // 发起 GET 请求
                .param("name", "Spring")) // 添加请求参数
                .andExpect(status().isOk()) // 断言状态码为 200
                .andExpect(content().string("Hello, Spring!")); // 断言响应内容
    }

    @Test
    public void testGreetEndpointWithDefault() throws Exception {
        mockMvc.perform(get("/api/greet"))
                .andExpect(status().isOk())
                .andExpect(content().string("Hello, World!"));
    }

    @Test
    public void testCreateUser() throws Exception {
        User newUser = new User("Alice", "alice@example.com");
        // 将 User 对象转换为 JSON 字符串
        String userJson = objectMapper.writeValueAsString(newUser);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON) // 设置请求内容类型
                .content(userJson)) // 设置请求体 JSON 内容
                .andExpect(status().isCreated()) // 断言状态码为 201(假设创建成功返回 201)
                .andExpect(jsonPath("$.name").value("Alice")) // 使用 JsonPath 断言返回的 JSON 字段值
                .andExpect(jsonPath("$.email").value("alice@example.com"));
    }
}

3.2 使用 TestRestTemplate (启动真实服务器)

这种方法会启动一个嵌入式的真实服务器(如 Tomcat),测试更接近于生产环境。

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.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.Assertions.assertThat;

// 使用 RANDOM_PORT 启动一个嵌入式服务器并监听随机端口,避免端口冲突
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ExampleControllerWithServerTest {

    @Autowired
    private TestRestTemplate restTemplate; // 注入 TestRestTemplate

    @Test
    public void testGreetEndpoint() {
        // 使用 TestRestTemplate 发起请求
        ResponseEntity<String> response = restTemplate.getForEntity("/api/greet?name=Spring", String.class);
        
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).isEqualTo("Hello, Spring!");
    }

    // 也可以测试 POST 请求
    @Test
    public void testCreateUser() {
        User newUser = new User("Bob", "bob@example.com");
        ResponseEntity<User> response = restTemplate.postForEntity("/api/users", newUser, User.class);
        
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().getName()).isEqualTo("Bob");
        assertThat(response.getBody().getEmail()).isEqualTo("bob@example.com");
    }
}

⚙️ 4. 关键注解和配置说明

下表总结了集成测试中常用的注解及其用途:

注解 说明 适用场景
@SpringBootTest 加载完整的 Spring 应用程序上下文,用于集成测试。 所有类型的集成测试
webEnvironment = WebEnvironment.MOCK 默认值。提供模拟的 Servlet 环境,不启动真实服务器。依赖 @AutoConfigureMockMvc 使用 MockMvc 进行控制层测试,速度较快
webEnvironment = WebEnvironment.RANDOM_PORT 启动嵌入式服务器并监听随机端口 使用 TestRestTemplateWebTestClient 进行测试
webEnvironment = WebEnvironment.DEFINED_PORT 使用 application.properties 中定义的端口(或默认的 8080)启动服务器。 需要固定端口的测试场景
@AutoConfigureMockMvc 自动配置 MockMvc 实例,用于模拟 MVC 请求。通常与 WebEnvironment.MOCK 结合使用。 使用 MockMvc 进行测试时必需
@Test JUnit Jupiter 注解,标记一个方法为测试方法。 所有测试方法
@Import 显式导入特定的配置类,用于测试。 需要覆盖特定配置或引入测试专用配置时

🧰 5. 处理依赖和模拟(Mocking)

在集成测试中,你通常希望测试完整的集成链,因此应尽量避免模拟(Mocking)。但如果某些外部依赖(如数据库、第三方服务)无法在测试环境中使用,或者你想隔离测试特定层,Spring Boot 提供了 @MockBean 注解来模拟 Bean。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import com.example.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
public class ExampleControllerWithMockServiceTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean // 模拟 UserService 这个 Bean,真实的 UserService 不会被调用
    private UserService userService;

    @Test
    public void testCreateUserWithMockService() throws Exception {
        User inputUser = new User("Charlie", "charlie@example.com");
        User savedUser = new User(1L, "Charlie", "charlie@example.com"); // 假设保存后有了 ID

        // 模拟 userService.save() 方法的行为
        when(userService.save(any(User.class))).thenReturn(savedUser);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(inputUser)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.id").value(1))
                .andExpect(jsonPath("$.name").value("Charlie"))
                .andExpect(jsonPath("$.email").value("charlie@example.com"));
    }
}

🚀 6. 运行测试

你可以使用 IDE 中的运行测试功能,或者通过 Maven 命令运行测试:

mvn test

Maven 会在 src/test/java 目录下查找测试类并运行。

💎 核心要点

  • 集成测试 (Integration Test):使用 @SpringBootTest 加载完整的应用程序上下文,测试各个组件之间的集成情况。
  • MockMvc:适用于模拟 MVC 环境的测试,无需启动真实服务器,速度快。 使用 perform 发起请求,andExpect 进行断言。
  • TestRestTemplate:适用于启动真实嵌入式服务器的测试,更接近真实环境。 直接发起 HTTP 请求并接收响应。
  • 随机端口:使用 WebEnvironment.RANDOM_PORT 可以避免测试时的端口冲突问题。
  • @MockBean:当你需要模拟应用程序上下文中的某个 Bean 时(例如模拟一个不易在测试环境中构建的外部服务),可以使用此注解。

希望这个指南能帮助你在 Spring Boot 3.x 中顺利编写控制器的集成测试!


文章来源:https://www.cnblogs.com/lori/p/19059332
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

上一篇:Java测试类、工具类与JavaBean对比解析
下一篇:没有了

相关文章

本站推荐

标签云