springboot 单元测试

发布于:2025-07-15 ⋅ 阅读:(18) ⋅ 点赞:(0)
测试类型 主要目的 Spring Context
单元测试 (Unit Test) 只测 Java 逻辑,完全隔离Spring ❌ 不用Spring
切片测试 (Slice Test) 只加载一部分 Spring Context ✅ 只加载需要的部分
集成测试 (Integration Test) 加载整个 Spring Boot 应用 ✅ 全部加载
端到端 (E2E) 启动全部+真实依赖 ✅ 全部加载 + 外部容器

👉 单元测试是最基础的一层:不依赖 Spring,运行最快,价值最大。


1. 单元测试

单元测试(Unit Test)定义

  • 测试一个类的最小可测单元

  • 不依赖 Spring

  • 不访问数据库、文件系统、网络

  • 通过模拟(Mock)替代外部依赖

特点:快、隔离、确保业务逻辑正确


2. Spring Boot 中单元测试依赖

虽然真正的单元测试不需要Spring,但通常使用Mockito + JUnit 5来Mock依赖:

在Maven里:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-test</artifactId>   

    <scope>test</scope>

</dependency>

✅ 包含:

  • JUnit 5 (Jupiter)、Mockito、AssertJ、Hamcrest


3. 单元测试写法详解

3.1 不依赖Spring的最简单写法

示例类

public class Calculator {

public int add(int a, int b) { return a + b; }

}

测试

import org.junit.jupiter.api.Test;

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

class CalculatorTest {

    @Test 
    void testAdd() { 
        Calculator calculator = new Calculator(); 
        assertEquals(5, calculator.add(2, 3)); 
    } 
}

特点:
✅ 没有Spring注解
✅ 没有IOC容器
✅ 执行毫秒级


3.2 有依赖的服务,使用 Mockito

业务场景
Service里依赖Repository:

@Service 
public class UserService { 
  private final UserRepository userRepository; 
  public UserService(UserRepository userRepository) { 
     this.userRepository = userRepository; 
  } 
  public User getUserById(Long id) { 
    return userRepository.findById(id).orElseThrow(() -> new NotFoundException()); 
  } 
}

单元测试目标
✅ 不启动Spring
✅ Mock UserRepository

测试写法

import org.junit.jupiter.api.Test; 
import org.mockito.InjectMocks; 
import org.mockito.Mock; 
import org.mockito.junit.jupiter.MockitoExtension; 
import static org.mockito.Mockito.when; 
import static org.junit.jupiter.api.Assertions.*; 

@ExtendWith(MockitoExtension.class) 
class UserServiceTest { 
  @Mock UserRepository userRepository; 
  @InjectMocks UserService userService; 
  @Test 
  void testGetUserById() { 
    User mockUser = new User(1L, "Alice"); 
    when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser)); 
    User result = userService.getUserById(1L); 
    assertEquals("Alice", result.getName()); 
  } 
}

✅ 依赖注入通过Mockito
✅ 无Spring Context启动
✅ 速度极快


4. 单元测试的Mock策略

Spring Boot推荐的单元测试Mock实践:

场景 工具
普通Bean 纯Java手写Mock或Mockito
接口依赖 Mockito
静态方法 Mockito (mockStatic)
构造函数/私有方法 Mockito(较新版本支持)或PowerMock

5. 为什么要Mock

隔离被测对象:UserServiceTest 只关心 UserService 的逻辑,不需要真正的数据库

提高速度:无IO

更容易写失败场景:模拟异常

可重复:永远稳定


6. @SpringBootTest 

✅ 是Spring Boot官方推荐的集成测试注解
✅ 启动完整的Spring Application Context
✅ 模拟「像生产里那样」启动你的Spring Bean、配置、自动装配

特点:

  • 测试的粒度:集成级

  • 加载速度:相对慢

  • 依赖注入:自动可用

  • 非常适合测试Service层、Controller层、整体协作


@SpringBootTest 注解核心原理

      ✅ 解析@SpringBootApplication
      ✅ 加载完整的Spring上下文
      ✅ 自动注入Bean
      ✅ 支持@MockBean替换Bean
      ✅ 可以模拟Web容器(随机端口、本地端口)

@SpringBootTest 常见用途

✅ Service层集成测试

  • 有些Service需要Autowired进来其他Service/Repository

  • 用@MockBean来mock下游依赖

✅ Controller端到端测试

  • 启动Web服务器

  • 用TestRestTemplate/RestAssured真正发HTTP

✅ 全局配置测试

  • 验证Spring Profile、Properties

✅ 事务测试

  • Spring Boot 测试默认有@Rollback

✅ 与Testcontainers结合

  • 启动真实PostgreSQL/MySQL


7. 单元测试 vs 切片测试 vs 集成测试

Spring Boot里常见误区是把一切都用@SpringBootTest,这是集成测试,非常慢。

建议分层:

层级 用途 注解 启动容器?
单元测试 只测Java逻辑 无或MockitoExtension
切片测试 测试部分Spring配置(MVC、JPA) @WebMvcTest、@DataJpaTest ✅(部分)
集成测试 测试整个Spring上下文 @SpringBootTest
端到端测试 测试整个系统+外部依赖 @SpringBootTest + Testcontainers

8. 推荐写法

✅ 80% 逻辑 → 纯单元测试
✅ 15% Spring 切片 → @WebMvcTest、@DataJpaTest
✅ 5% 全局集成 → @SpringBootTest / Testcontainers


9. 进阶:使用参数化测试

JUnit 5 支持:

@ParameterizedTest 
@CsvSource({"1,2,3", "5,7,12"}) 
void testAdd(int a, int b, int expected) { 
   Calculator calc = new Calculator(); 
   assertEquals(expected, calc.add(a, b)); 
}

✅ 避免重复代码
✅ 增加覆盖率


10. 单元测试常用断言库

✅ JUnit Assertions
✅ AssertJ (更好看、链式调用)
✅ Hamcrest

示例:

import static org.assertj.core.api.Assertions.assertThat; 
@Test 
void testSomething() { 
  String result = "hello"; 
  assertThat(result) 
   .isNotNull() 
   .startsWith("he") 
   .endsWith("lo"); 
}

11. CI/CD流程:单元测试

Maven

mvn test

✅ 无Spring启动的单元测试 → 非常适合频繁在CI里跑
✅ 成本最低


✅ 总结

Spring Boot 单元测试=真正Java单元测试 + Mock依赖


✅ 不要动不动用 @SpringBootTest,它本质是集成测试,不是严格意义的单元测试
✅ 依赖外部组件要Mock

✅ 用正确层次,减少维护成本
✅ 让测试成为生产力,而不是负担