如何正确使用 Mockito 模拟依赖方法而不触发真实调用

来源:3D模型作者:狼行天下头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何正确使用 Mockito 模拟依赖方法而不触发真实调用》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何正确使用 Mockito 模拟依赖方法而不触发真实调用》有用,将其分享出去将是对创作者最好的鼓励。

单元测试的核心目标是验证单个类或方法的行为,因此需要将其与外部依赖如数据库访问、远程接口调用、第三方服务等隔离开。Mockito框架可以通过创建模拟对象来替代真实依赖,从而控制依赖方法的返回结果,避免触发真实调用。不过如果用法不当,很容易出现模拟失效、真实方法被执行的情况,影响测试的稳定性和执行效率。

如何正确使用 Mockito 模拟依赖方法而不触发真实调用

Mockito模拟的基础准备

首先需要引入Mockito的依赖,以Maven项目为例,在pom.xml中添加如下依赖:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.0.0</version>
    <scope>test</scope>
</dependency>

编写测试类时,通常使用@Mock注解来创建模拟对象,配合MockitoAnnotations.openMocks(this)初始化,或者使用Mockito.mock(Class)方法手动创建模拟对象。以下是一个基础示例:

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;

// 待测试的依赖接口
interface UserService {
    String getUserNameById(Long id);
}

// 待测试的业务类
class OrderService {
    private UserService userService;
    public OrderService(UserService userService) {
        this.userService = userService;
    }
    public String getOrderOwner(Long userId) {
        return userService.getUserNameById(userId);
    }
}

public class OrderServiceTest {
    @Mock
    private UserService userService;
    private OrderService orderService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        orderService = new OrderService(userService);
    }
}

使用when thenReturn模拟有返回值的方法

对于返回值的依赖方法,最常用的模拟方式是when(依赖方法调用).thenReturn(返回值),这种方式会拦截对依赖方法的调用,直接返回指定的结果,不会触发真实调用。示例如下:

@Test
void testGetOrderOwner_WithValidUserId() {
    // 模拟userService的getUserNameById方法,当传入1L时返回"张三"
    when(userService.getUserNameById(1L)).thenReturn("张三");
    // 调用待测试方法
    String result = orderService.getOrderOwner(1L);
    // 验证结果
    assert "张三".equals(result);
    // 验证依赖方法确实被调用了一次
    verify(userService, times(1)).getUserNameById(1L);
}

需要注意,when后面的方法调用必须是模拟对象的方法,如果是真实对象的方法调用,就会触发真实执行。另外如果传入的参数不匹配模拟时设定的条件,也不会返回模拟的结果,而是返回模拟对象的默认值,比如String类型返回null,int类型返回0。

使用doReturn when模拟避免真实调用

当依赖方法是被监视的对象(spy)的方法,或者方法调用会抛出无法捕获的异常时,使用when thenReturn可能会先触发一次真实调用,此时需要改用doReturn(返回值).when(模拟对象).方法调用的语法,这种写法不会先执行真实方法。示例如下:

@Test
void testGetOrderOwner_WithDoReturn() {
    // 使用doReturn模拟,不会触发真实调用
    doReturn("李四").when(userService).getUserNameById(2L);
    String result = orderService.getOrderOwner(2L);
    assert "李四".equals(result);
}

这种方式尤其适合模拟void方法、受检异常方法,或者需要避免方法调用副作用的场景。

模拟void无返回值的方法

对于无返回值的依赖方法,无法使用thenReturn,需要使用doNothingdoThrow等语法来模拟。默认情况下,模拟对象的void方法调用不会执行任何操作,也就是doNothing是默认行为,如果需要模拟抛出异常,可以使用doThrow。示例如下:

interface LogService {
    void recordLog(String content);
}

class OrderService {
    private LogService logService;
    public OrderService(LogService logService) {
        this.logService = logService;
    }
    public void createOrder() {
        logService.recordLog("创建订单");
    }
}

@Test
void testCreateOrder_WithVoidMethod() {
    LogService logService = mock(LogService.class);
    OrderService orderService = new OrderService(logService);
    // 模拟void方法,默认doNothing,无需额外配置
    orderService.createOrder();
    // 验证方法被调用
    verify(logService).recordLog("创建订单");
}

@Test
void testCreateOrder_WithVoidMethodThrowException() {
    LogService logService = mock(LogService.class);
    OrderService orderService = new OrderService(logService);
    // 模拟void方法抛出异常
    doThrow(new RuntimeException("日志写入失败")).when(logService).recordLog(anyString());
    // 调用时会抛出异常
    try {
        orderService.createOrder();
    } catch (RuntimeException e) {
        assert "日志写入失败".equals(e.getMessage());
    }
}

参数匹配规则与避免真实调用

Mockito提供了多种参数匹配器,比如any()eq()anyLong()等,使用匹配器时需要注意,如果一个参数使用了匹配器,所有参数都必须使用匹配器,否则会导致模拟失效,甚至触发真实调用。示例如下:

@Test
void testGetOrderOwner_WithMatcher() {
    // 正确用法:所有参数都使用匹配器
    when(userService.getUserNameById(anyLong())).thenReturn("王五");
    String result1 = orderService.getOrderOwner(3L);
    assert "王五".equals(result1);

    // 错误用法:混合使用具体值和匹配器,会导致模拟不生效
    // when(userService.getUserNameById(3L, anyString())).thenReturn("赵六"); // 编译错误或运行时异常
}

如果模拟时使用了过于宽泛的匹配器,比如any(),可能会导致非预期的调用也被匹配到,因此建议尽量使用精确的匹配条件,减少模拟的意外覆盖范围。

验证调用行为而不触发真实逻辑

模拟依赖方法后,通常需要验证依赖方法是否按照预期被调用,此时使用verify方法即可,verify只会检查模拟对象的方法调用记录,不会触发真实调用。可以指定调用的次数、参数等条件:

@Test
void testGetOrderOwner_VerifyCall() {
    when(userService.getUserNameById(1L)).thenReturn("张三");
    orderService.getOrderOwner(1L);
    // 验证方法被调用一次
    verify(userService, times(1)).getUserNameById(1L);
    // 验证方法没有被调用传入2L的情况
    verify(userService, never()).getUserNameById(2L);
}

常见错误与避坑指南

  • 不要对真实对象调用when方法:如果对new出来的真实对象使用when,会直接执行真实方法,需要先将真实对象转为spy对象,或者使用doReturn语法。
  • 避免模拟final类、final方法、私有方法:Mockito默认无法模拟这些类型的方法,需要开启对应的扩展功能,否则模拟会失效触发真实调用。
  • 模拟配置要在调用待测试方法之前完成:如果先调用了待测试方法,依赖方法已经执行过真实逻辑,再配置模拟也不会生效。
  • 不要过度模拟:只模拟待测试类的直接依赖,不要模拟无关的对象,否则会让测试失去对真实逻辑的验证作用。

通过合理使用Mockito的模拟语法,明确区分when thenReturndoReturn when的适用场景,注意参数匹配规则,就可以有效避免依赖方法的真实调用,写出独立、稳定、高效的单元测试。

Mockito单元测试依赖模拟verifywhen修改时间:2026-06-14 18:30:24

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。