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 使用
相关推荐
左手の明天7 分钟前
SLIP 协议封装与解析详解:原理、C 代码与实战案例
linux·c语言·开发语言·c++
shuoshuohaohao11 分钟前
MyBatis 小知识点
java·spring·mybatis
海上小飞龙25 分钟前
【KMP算法-下篇】同一道题:Java 库函数 2600 微秒,手写 KMP 16 微秒
java·开发语言·算法
朝朝辞暮i42 分钟前
C++ 第 12 课:局部变量、作用域、变量生命周期
开发语言·c++·算法
狗凯之家源码网1 小时前
淘客口令落地页生成器 PHP 版深度评测
开发语言·php
画绛集美术1 小时前
本地化部署LLM用于美术史问答:一间美术教室的实践记录
开发语言·php
谢亮_vipxieliang1 小时前
泛型擦除、通配符与 PECS 一次讲透
java·面试
八解毒剂1 小时前
【计组】中央处理器CPU
java·开发语言·计算机组成原理
FakeOccupational2 小时前
【电路笔记 信号】DBPSK 波形查找表+脉冲成形(升余弦+根升余弦滤波)
开发语言·笔记
打工仔折腾 AI2 小时前
工业场景下时序库与实时计算一体化架构选型实践对比
java·开发语言·后端·python·性能优化·架构·ai agent 实战