🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
在这里插入图片描述

SpringBoot单元测试进阶:Mockito+JUnit5的23种测试场景

一、引言

在软件开发过程中,单元测试是保证代码质量、提高开发效率的重要手段。Spring Boot作为目前流行的Java开发框架,结合Mockito和JUnit 5可以让单元测试变得更加高效和灵活。本文将深入介绍使用Mockito和JUnit 5在Spring Boot项目中进行单元测试的23种常见场景,帮助技术人员提升单元测试的能力。

二、环境准备

2.1 添加依赖

在pom.xml文件中添加必要的依赖:

<dependencies>
    <!-- Spring Boot Starter Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Mockito Core -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- JUnit 5 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

三、基本测试场景

3.1 简单方法返回值模拟

假设我们有一个UserService类,其中有一个getUserName方法:

public class UserService {
    public String getUserName() {
        return "default";
    }
}

使用Mockito和JUnit 5进行单元测试:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class UserServiceTest {
    @Test
    public void testSimpleMock() {
        UserService userService = mock(UserService.class);
        when(userService.getUserName()).thenReturn("testUser");
        assertEquals("testUser", userService.getUserName());
    }
}

3.2 方法抛出异常模拟

修改UserService类,添加一个可能抛出异常的方法:

public class UserService {
    public String getUserName() {
        return "default";
    }

    public void throwException() throws Exception {
        throw new Exception("Test exception");
    }
}

测试代码:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class UserServiceTest {
    @Test
    public void testExceptionMock() {
        UserService userService = mock(UserService.class);
        try {
            when(userService.throwException()).thenThrow(new Exception("Mocked exception"));
            assertThrows(Exception.class, () -> userService.throwException());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3 方法多次调用不同返回值

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class UserServiceTest {
    @Test
    public void testMultipleReturns() {
        UserService userService = mock(UserService.class);
        when(userService.getUserName()).thenReturn("first", "second");
        assertEquals("first", userService.getUserName());
        assertEquals("second", userService.getUserName());
    }
}

四、参数匹配场景

4.1 精确参数匹配

假设UserService类有一个根据用户ID获取用户名的方法:

public class UserService {
    public String getUserNameById(int id) {
        return "default";
    }
}

测试代码:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class UserServiceTest {
    @Test
    public void testExactParameterMatch() {
        UserService userService = mock(UserService.class);
        when(userService.getUserNameById(1)).thenReturn("user1");
        assertEquals("user1", userService.getUserNameById(1));
    }
}

4.2 任意参数匹配

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.anyInt;

public class UserServiceTest {
    @Test
    public void testAnyParameterMatch() {
        UserService userService = mock(UserService.class);
        when(userService.getUserNameById(anyInt())).thenReturn("anyUser");
        assertEquals("anyUser", userService.getUserNameById(2));
    }
}

4.3 自定义参数匹配器

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.mockito.ArgumentMatcher;

public class UserServiceTest {
    @Test
    public void testCustomParameterMatcher() {
        UserService userService = mock(UserService.class);
        ArgumentMatcher<Integer> positiveMatcher = i -> i > 0;
        when(userService.getUserNameById(org.mockito.ArgumentMatchers.argThat(positiveMatcher))).thenReturn("positiveUser");
        assertEquals("positiveUser", userService.getUserNameById(3));
    }
}

五、验证方法调用场景

5.1 验证方法是否被调用

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class UserServiceTest {
    @Test
    public void testMethodInvocation() {
        UserService userService = mock(UserService.class);
        userService.getUserName();
        verify(userService).getUserName();
    }
}

5.2 验证方法调用次数

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

public class UserServiceTest {
    @Test
    public void testMethodInvocationTimes() {
        UserService userService = mock(UserService.class);
        userService.getUserName();
        userService.getUserName();
        verify(userService, times(2)).getUserName();
    }
}

5.3 验证方法未被调用

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

public class UserServiceTest {
    @Test
    public void testMethodNotInvoked() {
        UserService userService = mock(UserService.class);
        verify(userService, never()).throwException();
    }
}

六、模拟对象注入场景

6.1 使用@Mock注解

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

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
    @Mock
    private UserService userService;

    @Test
    public void testMockAnnotation() {
        when(userService.getUserName()).thenReturn("mockUser");
        assertEquals("mockUser", userService.getUserName());
    }
}

6.2 使用@InjectMocks注解

假设我们有一个UserController类依赖于UserService:

public class UserController {
    private UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    public String getUserName() {
        return userService.getUserName();
    }
}

测试代码:

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

@ExtendWith(MockitoExtension.class)
public class UserControllerTest {
    @Mock
    private UserService userService;

    @InjectMocks
    private UserController userController;

    @Test
    public void testInjectMocksAnnotation() {
        when(userService.getUserName()).thenReturn("mockUser");
        assertEquals("mockUser", userController.getUserName());
    }
}

七、Spy对象场景

7.1 创建Spy对象

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

public class UserServiceTest {
    @Test
    public void testSpyObject() {
        UserService userService = new UserService();
        UserService spyService = spy(userService);
        when(spyService.getUserName()).thenReturn("spyUser");
        assertEquals("spyUser", spyService.getUserName());
    }
}

7.2 Spy对象调用真实方法

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.doCallRealMethod;

public class UserServiceTest {
    @Test
    public void testSpyCallRealMethod() {
        UserService userService = new UserService();
        UserService spyService = spy(userService);
        doCallRealMethod().when(spyService).getUserName();
        assertEquals("default", spyService.getUserName());
    }
}

八、异步方法测试场景

8.1 模拟异步方法返回值

假设UserService类有一个异步方法:

import java.util.concurrent.CompletableFuture;

public class UserService {
    public CompletableFuture<String> getUserNameAsync() {
        return CompletableFuture.completedFuture("default");
    }
}

测试代码:

import org.junit.jupiter.api.Test;
import java.util.concurrent.CompletableFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class UserServiceTest {
    @Test
    public void testAsyncMethod() throws Exception {
        UserService userService = mock(UserService.class);
        CompletableFuture<String> future = CompletableFuture.completedFuture("asyncUser");
        when(userService.getUserNameAsync()).thenReturn(future);
        assertEquals("asyncUser", userService.getUserNameAsync().get());
    }
}

九、Spring Boot集成测试场景

9.1 测试Spring Bean

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.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

@SpringBootTest
public class UserServiceSpringTest {
    @Autowired
    private UserController userController;

    @MockBean
    private UserService userService;

    @Test
    public void testSpringBean() {
        when(userService.getUserName()).thenReturn("springMockUser");
        assertEquals("springMockUser", userController.getUserName());
    }
}

9.2 测试RESTful接口

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.when;
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;

@WebMvcTest(UserController.class)
public class UserControllerRestTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    public void testRestfulApi() throws Exception {
        when(userService.getUserName()).thenReturn("restMockUser");
        mockMvc.perform(get("/user/name"))
               .andExpect(status().isOk())
               .andExpect(content().string("restMockUser"));
    }
}

十、总结

通过以上23种测试场景的介绍,我们可以看到Mockito和JUnit 5在Spring Boot单元测试中的强大功能。合理运用这些技巧,可以提高单元测试的覆盖率和质量,确保代码的稳定性和可维护性。希望本文对技术人员在Spring Boot项目的单元测试方面有所帮助。

更多推荐