mockito笔记

文章目录

引入maven依赖(推荐用spring的方式)

spring-boot-starter-test已经自带JUnit5 + Mockito,不需要单独引入mockito。

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

单元测试类

java 复制代码
// 1. 引入 JUnit5 和 Mockito 的扩展
@ExtendWith(MockitoExtension.class)
@DisplayName("用户服务 (UserService) 单元测试")
class UserServiceTest {

    // 2. 创建虚拟的依赖对象(替身演员)
    @Mock
    private UserRepository userRepository;

    @Mock
    private EmailService emailService;

    // 3. 创建被测试的真实对象,并自动注入 Mock 对象
    @InjectMocks
    private UserService userService;

    // ================= 用户注册场景分组 =================
    @Nested
    @DisplayName("用户注册场景")
    class RegisterUserTest {

        @Test
        @DisplayName("成功注册:保存数据并发送欢迎邮件")
        void testRegisterUser_Success() {
            // ========== Arrange (准备) ==========
            String username = "张三";
            User mockUser = new User(username);
            when(userRepository.save(any(User.class))).thenReturn(mockUser);
            doNothing().when(emailService).sendWelcomeEmail(any(User.class));

            // ========== Act (执行) ==========
            User result = userService.register(username);

            // ========== Assert (断言) ==========
            assertNotNull(result);
            assertEquals("张三", result.getUsername());
            verify(userRepository, times(1)).save(any(User.class));
            verify(emailService).sendWelcomeEmail(any(User.class));
        }

        // 新增的测试方法:异常场景
        @Test
        @DisplayName("注册失败:用户名已存在时抛出异常")
        void testRegisterUser_AlreadyExists() {
            // ========== Arrange (准备) ==========
            String username = "李四";
            // 模拟数据库抛出用户名已存在的异常
            when(userRepository.save(any(User.class)))
                    .thenThrow(new BusinessException("用户名已存在"));

            // ========== Act & Assert (执行与断言) ==========
            // 验证调用 register 方法时,确实抛出了预期的异常
            BusinessException exception = assertThrows(
                    BusinessException.class, 
                    () -> userService.register(username)
            );
            assertEquals("用户名已存在", exception.getMessage());
            
            // 验证因为保存失败了,所以不应该触发发送邮件的逻辑
            verify(emailService, never()).sendWelcomeEmail(any(User.class));
        }
    }
}

常用注解

@ExtendWith(MockitoExtension.class)

JUnit5专属。用于启用Mockito功能,自动初始化Mock对象并管理生命周期。(注:如果是老版本JUnit4,则使用@RunWith(MockitoJUnitRunner.class))

@Mock

创建一个依赖的"替身"(Mock对象),它的方法默认返回空值,需要通过when().thenReturn()来定义行为。

@InjectMocks

实例化你要测试的真实类(如UserService),并自动把带有@Mock注解的替身注入进去。

@Test

标记该方法为一个单元测试用例。

@DisplayName

为测试方法提供一个自定义的显示名称(支持中文),让测试报告更易读。

@Nested

@Nested的核心作用有3点:

1、逻辑分组(建文件夹)

把相关的测试用例(@Test)按业务场景分门别类,让测试报告呈现清晰的树状结构。

2、共享上下文(省代码)

内部类可以直接使用外部类的@Mock和@InjectMocks对象,不用重复写。

3、独立生命周期(互不干扰)

每个嵌套类可以有自己专属的@BeforeEach(初始化)和@AfterEach(清理)方法,互不影响。

@InjectMocks和@Mock区别
特性 @Mock @InjectMocks
作用 创建依赖的虚拟对象 创建被测类实例并注入依赖
是否需要打桩
是否调用真实逻辑 否(默认返回空值) 是(调用真实方法,但依赖是 Mock)
使用场景 隔离外部依赖 组装被测对象
是否必须配合使用 否(可单独使用) 通常需要配合 @Mock 使用
相关推荐
悟天特斯44 分钟前
AI驱动的楼宇节能:从粗放管控到精准降碳的实践路径
开发语言·人工智能·python·物联网
名字还没想好☜1 小时前
Python 用 tempfile 安全创建临时文件:NamedTemporaryFile、TemporaryDirectory 与别自己拼 /tmp 的坑
开发语言·后端·python·安全·编程语言
lvshuocool1 小时前
golang 多版本管理工具 -- g
开发语言·后端·golang
matlab代码1 小时前
基于matlab数字图像处理的指纹识别系统 点线特征(GUI界面)【源码69期】
开发语言·matlab
Doubbbbbbble云1 小时前
基于空间局部性的排序算法性能重构思路4
java·重构·排序算法
linx2951 小时前
单元三 · 那 C 的底层知识怎么办
c语言·开发语言·数据结构·c++·嵌入式硬件
MetaLite1 小时前
Java 时间处理的两个坑:单位误判,半秒算成一秒
java·开发语言·前端
黄骨鱼骨1 小时前
DatI:给AI接入数据库,Agent时代的NL2SQL问数与多维表格应该怎么做
java·database·text2sql·nl2sql·chatbi·mcp·data agent
Wang's Blog2 小时前
Java框架快速入门: Spring Security+OAuth2之实现UserDetails与GrantedAuthority深度定制
java·spring·mybatis