在SpringBoot中编写Mock单元测试

本文介绍了如何在SpringBoot应用中编写Mock单元测试,包括新建测试类、添加依赖、理解Mock概念以及Mockito的常用方法。Mockito是一个流行的Java单元测试框架,允许创建mock对象来模拟真实对象的行为,以便于测试。文中详细讲解了@Test、@Before、@After等注解的作用,并展示了Mock和Spy的使用示例。

1. 新建测试类

在IDEA中写好的类中,右建GO TO,选择TEST,如果没有对应的单元测试类就选择创建。

在创建该类的单元测试类前,先讲一下这几个基本注解:

@Test:使用该注解标注的public void方法会表示为一个测试方法;
@BeforeClass:表示在类中的任意public static void方法执行之前执行;
@AfterClass:表示在类中的任意public static void方法之后执行;
@Before:表示在任意使用@Test注解标注的public void方法执行之前执行;
@After:表示在任意使用@Test注解标注的public void方法执行之后执行;

2. 添加依赖

新建的springBoot项目中默认包含了spring-boot-starter-test的依赖,如果没有包含可自行在pom.xml中添加依赖。

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

3. Mock的概念

所谓的mock就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,以达到两大目的:

  1. 验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等
  2. 指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作

使用Mock之前,需要在@Before或@BeforeClass对应的方法中添加如下,表示添加mock注解初始化。

MockitoAnnotations.initMocks(this);

另外需要补充以下几个常用的测试注解:

  • @InjectMocks:通过创建一个实例,它可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。

  • @Mock:对函数的调用均执行mock(即虚假函数),不执行真正部分。

  • @Spy:对函数的调用均执行真正部分。

Mockito中的Mock和Spy都可用于拦截那些尚未实现或不期望被真实调用的对象和方法,并为其设置自定义行为。二者的区别在于Mock不真实调用,Spy会真实调用。

  • @mock与@spy使用示例

(1)被测试类

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

(2)测试类

import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*;

public class MockitoDemo {
  	@Spy
    private ExampleService spyExampleService;
  
  	@Mock
  	private ExampleService mockExampleService;

    // 测试 spy
    @Test
    public void test_spy() {
      
        // 默认会走真实方法
        Assert.assertEquals(3, spyExampleService.add(1, 2));

        // 打桩后,不会走了
        when(spyExampleService.add(1, 2)).thenReturn(10);
        Assert.assertEquals(10, spyExampleService.add(1, 2));

        // 但是参数不匹配的调用,依然走真实方法
        Assert.assertEquals(3, spyExampleService.add(2, 1));
      
    }

    // 测试 mock
    @Test
    public void test_mock() {

        // 默认返回结果是返回类型int的默认值
        Assert.assertEquals(0, mockExampleService.add(1, 2));

    }
}
  • @InjectMock和@Mock使用示例

(1)被测试类

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

class InjectMockExampleService {
  	
  	@Autowired
  	private MockExampleService mockExampleService;
      
    int add(int a, int b) {
        return mockExampleService.add(a, b);
    }
  
}

(2)测试类

import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.*;

public class MockitoDemo {
  	@InjectMock
    private InjectMockExampleService injectMockExampleService;
  
  	@Mock
  	private MockExampleService mockExampleService;
  	
  	// 初始化汉顺
  	@Before
  	public void init() throw Exception {
    		MockitoAnnotations.initMocks(this);
    }

    // 使用Mockito模拟
    @Test
    public void test() {
      
      	// 模拟MockExampleServiceadd函数对于任何参数返回都为10
				when(mockExampleService.add(anyInt(), anyInt())).thenReturn(10);
      	// InjectMock会走真实的add方法,只不过mock会返回一个模拟的结果
        Assert.assertEquals(10, injectMockExampleService.add(1, 2));

    }
}

4. 常用的 Mockito 方法

Mockito的使用,一般有以下几种组合:参考链接

  • do/when:包括doThrow(…).when(…)/doReturn(…).when(…)/doAnswer(…).when(…)
  • given/will:包括given(…).willReturn(…)/given(…).willAnswer(…)
  • when/then: 包括when(…).thenReturn(…)/when(…).thenAnswer(…)/when(…).thenThrow(…)

Mockito 有多种匹配函数,部分如下:

函数名匹配类型
any()所有对象类型
anyInt()基本类型 int、非 null 的 Integer 类型
anyChar()基本类型 char、非 null 的 Character 类型
anyShort()基本类型 short、非 null 的 Short 类型
anyBoolean()基本类型 boolean、非 null 的 Boolean 类型
anyDouble()基本类型 double、非 null 的 Double 类型
anyFloat()基本类型 float、非 null 的 Float 类型
anyLong()基本类型 long、非 null 的 Long 类型
anyByte()基本类型 byte、非 null 的 Byte 类型
anyString()String 类型(不能是 null)
anyList()List 类型(不能是 null)
anyMap()Map<K, V>类型(不能是 null)

5. 参考

  • https://blog.youkuaiyun.com/yangshengwei230612/article/details/104753603/
  • https://blog.youkuaiyun.com/jieyingxiao/article/details/98217955
  • https://blog.youkuaiyun.com/u011047968/article/details/91970070?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control
  • https://www.letianbiji.com/java-mockito/mockito-spy.html
<think> 我们之前讨论了在IntelliJ IDEA中创建和配置Spring Boot项目,现在用户询问如何编写单元测试类。 根据Spring Boot的惯例,我们通常使用Spring Boot Test框架(基于JUnit)来编写单元测试。 步骤概述: 1. 确保项目已包含必要的测试依赖(如spring-boot-starter-test) 2. 在src/test/java目录下创建测试类 3. 使用注解(如@SpringBootTest)配置测试类 4. 编写测试方法,并使用@Test注解标记 5. 利用MockMvc或依赖注入来测试组件 由于用户引用中提到了创建项目的方法,我们可以假设用户已经创建好了项目。因此,重点放在创建测试类上。 详细步骤: 1. 依赖配置:在pom.xml中应该有spring-boot-starter-test依赖(通常创建项目时已自动包含)。 2. 创建测试类:在src/test/java下找到与主类相同包结构(或可以不同,但通常建议相同),右键新建一个Java类。 3. 编写测试类: - 使用@SpringBootTest注解来加载应用程序上下文(用于集成测试)。 - 对于单元测试(不启动整个Spring容器),可以使用更轻量的方式,比如使用Mockito来模拟依赖。 4. 示例:测试一个REST控制器。 考虑到用户可能想测试不同层次的组件(如Controller、Service、Repository),我们给出一个简单的Controller测试示例。 示例:测试一个简单的HelloWorld控制器。 假设我们有一个控制器: ```java @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello, World!"; } } ``` 对应的测试类: ```java // 在src/test/java的相同包下创建HelloControllerTest 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.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc public class HelloControllerTest { @Autowired private MockMvc mockMvc; @Test public void testHello() throws Exception { mockMvc.perform(get("/hello")) .andExpect(status().isOk()) .andExpect(content().string("Hello, World!")); } } ``` 5. 运行测试:在IDEA中,可以直接在测试方法旁边点击运行按钮(绿色的箭头)来运行单个测试,或者运行整个测试类。 另外,如果只是想对Service层进行单元测试(不涉及Web层),则可以不使用@SpringBootTest,而是使用Mockito模拟依赖。 示例:Service单元测试 假设有一个Service: ```java @Service public class MyService { public String doSomething() { return "Done"; } } ``` 测试类: ```java import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class MyServiceTest { @Test public void testDoSomething() { MyService myService = new MyService(); String result = myService.doSomething(); assertEquals("Done", result); } } ``` 如果需要模拟依赖(比如MyService依赖其他组件),则可以使用@Mock和@InjectMocks注解(结合Mockito)。 总结: - 单元测试(隔离测试):直接实例化类,测试方法。 - 集成测试:使用@SpringBootTest加载上下文,使用MockMvc测试Web层,或者注入Service/Repository进行测试。 注意:在Spring Boot 2.2及以上版本,默认使用JUnit 5(Jupiter),因此我们使用org.junit.jupiter.api.Test。 根据用户的问题,我们重点介绍了在IntelliJ IDEA中编写单元测试类的方法。用户可以根据需要选择测试策略。 相关问题: 1. 如何在Spring Boot测试中模拟依赖? 2. Spring Boot Test中@SpringBootTest和@WebMvcTest有什么区别? 3. 如何只加载部分配置进行单元测试? </think> ### 在 IntelliJ IDEA 中编写 Spring Boot 单元测试的方法 以下是详细的步骤指南(基于 Spring Boot 2.x+ 和 JUnit 5): --- #### 1. **添加测试依赖** 确保 `pom.xml` 包含测试依赖(Spring Initializr 创建的项目通常已自动包含): ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> ``` --- #### 2. **创建测试类** 在 `src/test/java` 目录下创建与被测类**同名+Test后缀**的测试类(IDEA 可自动生成): 1. 光标定位到要测试的类(如 `UserService.java`) 2. 按 `Ctrl+Shift+T` (Windows/Linux) 或 `⌘+Shift+T` (Mac) 3. 选择 **Create New Test** 4. 勾选要测试的方法,点击 **OK** ![测试类创建示意](https://example.com/test-class-creation.png)[^2] --- #### 3. **编写基础测试(示例)** ```java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest // 加载完整应用上下文 class UserServiceTest { @Autowired private UserService userService; // 注入待测试组件 @Test void testGetUserById() { User user = userService.getUserById(1L); assertNotNull(user); assertEquals("admin", user.getUsername()); } } ``` --- #### 4. **常用测试类型** | **测试场景** | **注解** | **适用层级** | |--------------------|----------------------|-------------------| | 完整集成测试 | `@SpringBootTest` | 全栈测试 | | 仅Web层测试 | `@WebMvcTest` | Controller层 | | 数据层测试 | `@DataJpaTest` | Repository层 | | 纯业务逻辑测试 | `@ExtendWith(MockitoExtension.class)` | Service层(无依赖)| --- #### 5. **Mock 依赖示例** 使用 Mockito 隔离测试 Service 层: ```java @ExtendWith(MockitoExtension.class) class OrderServiceTest { @Mock private PaymentGateway paymentGateway; // 模拟依赖 @InjectMocks private OrderService orderService; // 注入待测对象 @Test void testCreateOrder_Success() { // 配置模拟行为 when(paymentGateway.process(anyDouble())).thenReturn(true); Order order = orderService.createOrder(100.0); assertTrue(order.isPaid()); } } ``` --- #### 6. **运行测试** - 单个测试方法:点击方法左侧的 ▶️ 图标 - 整个测试类:点击类名左侧的 ▶️ 图标 - 所有测试:右键 `src/test/java` > **Run Tests** ![测试运行界面](https://example.com/test-run-window.png)[^2] --- #### 7. **最佳实践建议** 1. 测试命名规范:`methodName_StateUnderTest_ExpectedBehavior` 2. 使用 `@TestConfiguration` 提供专用测试 Bean 3. 数据库测试添加 `@Transactional` 避免脏数据 4. 利用 `TestRestTemplate` 测试 REST API 5. 使用 `@MockBean` 替换 Spring 上下文中的 Bean > 提示:IDEA 的 **Coverage 工具**(运行测试时勾选 ☑️ Run with Coverage)可直观显示代码覆盖率[^4]。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值