如何在 Mockito 中正确模拟内部实例方法调用

千浩酱_2000

千浩酱_2000

2026-06-23

152人浏览

原创

本文讲解当被测类在方法内部直接 new 出依赖对象(如 MyHttpClient)时,如何通过重构+接口抽象实现可测试性,避免无法 mock 的困境,并提供两种专业级解决方案。

本文讲解当被测类在方法内部直接 new 出依赖对象(如 `myhttpclient`)时,如何通过重构+接口抽象实现可测试性,避免无法 mock 的困境,并提供两种专业级解决方案。

在使用 Mockito 进行单元测试时,一个常见却棘手的问题是:被测方法内部通过 new 关键字创建了协作对象(如 MyHttpClient),导致该对象的实例方法(如 performCall())无法被 Mockito 拦截和模拟。你当前的 MyDataSourceImpl.execute() 方法正是如此——它在运行时动态构造 MyHttpClient,而 Mockito 默认只能 mock 由 Spring 容器管理或显式注入的依赖,对 new 出来的对象无能为力。

你尝试添加 @Mock private MyHttpClient myClient; 并设置 when(myClient.performCall()).thenReturn(...) 是无效的,因为测试中实际执行的是 new MyHttpClient(...).performCall(),与你声明的 mock 实例完全无关——它们是两个不同的对象。

✅ 推荐方案一:依赖注入重构(首选,符合测试友好设计)

最根本、最可持续的解法是将硬编码的 new 调用升级为依赖注入,让 MyHttpClient 成为 MyDataSourceImpl 的可注入协作方:

// 1. 修改被测类:注入 HttpClient(而非在方法内 new)
public class MyDataSourceImpl implements MyDataSource {
    @Autowired
    private PropertieService propertieService;

    // 新增:注入客户端(可通过构造器注入更佳,此处保留 @Autowired 示例)
    @Autowired
    private MyHttpClient myClient; // ← 不再在 execute() 中 new!

    public HttpResponse execute(String url) {
        // 直接调用已注入的 client,便于 mock
        HttpResponse response = myClient.performCall(url, propertieService.getProperties());
        this.someOtherStuff(response);
        return response;
    }
}

对应测试只需注入 mock 客户端即可:

@RunWith(MockitoJUnitRunner.class)
public class MyDataSourceImplTest {
    @InjectMocks
    private MyDataSourceImpl myDataSourceImpl;

    @Mock
    private PropertieService propertieService;

    @Mock
    private MyHttpClient myClient; // ← 现在有效!Mockito 会将其注入到 myDataSourceImpl

    @Test
    public void simpleCall() {
        // 给 stub 依赖
        when(propertieService.getProperties()).thenReturn(new HashMap());
        HttpResponse mockResponse = new HttpResponse(); // 假设构造合法
        when(myClient.performCall("https://nothing.com", anyMap())).thenReturn(mockResponse);

        // 执行 & 验证
        HttpResponse response = myDataSourceImpl.execute("https://nothing.com");
        assertEquals(mockResponse, response);
    }
}

✅ 优势:代码更松耦合、职责清晰、天然可测;✅ 符合 Spring 最佳实践;✅ 无需额外适配层。

SingClaw
SingClaw

SingClaw是一款会记忆的 AI 数据桌面助手。

下载

✅ 推荐方案二:引入工厂/策略接口(适用于无法修改 MyHttpClient 构造逻辑的场景)

若因历史约束无法将 MyHttpClient 改为 Spring Bean(例如它依赖非 Spring 管理的资源),则应引入抽象层隔离对象创建

// 1. 定义可 mock 的行为契约
public interface HttpClientCaller {
    HttpResponse performCall(String url, Map<string string> props);
}

// 2. 提供默认实现(适配器)
@Component
public class DefaultHttpClientCaller implements HttpClientCaller {
    private final PropertieService propertieService;

    public DefaultHttpClientCaller(PropertieService propertieService) {
        this.propertieService = propertieService;
    }

    @Override
    public HttpResponse performCall(String url, Map<string string> props) {
        return new MyHttpClient(url, props).performCall(); // ← 封装 new 逻辑
    }
}

// 3. 修改被测类:依赖接口而非具体类
public class MyDataSourceImpl implements MyDataSource {
    @Autowired
    private PropertieService propertieService;

    @Autowired
    private HttpClientCaller httpClientCaller; // ← 注入接口

    public HttpResponse execute(String url) {
        HttpResponse response = httpClientCaller.performCall(
            url, 
            propertieService.getProperties()
        );
        this.someOtherStuff(response);
        return response;
    }
}</string></string>

测试时 mock 接口即可:

@Test
public void simpleCallWithAdapter() {
    when(propertieService.getProperties()).thenReturn(new HashMap());

    HttpResponse mockResponse = new HttpResponse();
    when(httpClientCaller.performCall("https://nothing.com", anyMap()))
        .thenReturn(mockResponse);

    HttpResponse result = myDataSourceImpl.execute("https://nothing.com");
    assertEquals(mockResponse, result);
}

⚠️ 注意:避免使用 PowerMock 等字节码增强工具 mock 构造函数——它增加测试脆弱性、降低可维护性,且违背“测试驱动设计”倡导的可测性即设计质量指标。

总结

  • 不要在业务方法中 new 关键协作对象:这是可测试性的最大障碍;
  • 优先采用依赖注入重构:将 new 上提至容器或构造器,让 Mockito 自然生效;
  • 次选接口抽象 + 适配器模式:用一层轻量接口解耦创建逻辑,保持测试可控性;
  • 所有方案的核心思想一致:将“创建”与“使用”分离,使“使用”部分可被替换(mock)

遵循以上原则,你的 MyDataSourceImpl 不仅能被可靠测试,其设计本身也将更健壮、更易演进。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
软件测试常用工具
软件测试常用工具

软件测试常用工具有Selenium、JUnit、Appium、JMeter、LoadRunner、Postman、TestNG、LoadUI、SoapUI、Cucumber和Robot Framework等等。测试人员可以根据具体的测试需求和技术栈选择适合的工具,提高测试效率和准确性 。

2023.10.13

3930

8

java测试工具有哪些
java测试工具有哪些

java测试工具有JUnit、TestNG、Mockito、Selenium、Apache JMeter和Cucumber。php还给大家带来了java有关的教程,欢迎大家前来学习阅读,希望对大家能有所帮助。

2023.10.23

2381

10

Java 单元测试
Java 单元测试

本专题聚焦 Java 在软件测试与持续集成流程中的实战应用,系统讲解 JUnit 单元测试框架、Mock 数据、集成测试、代码覆盖率分析、Maven 测试配置、CI/CD 流水线搭建(Jenkins、GitHub Actions)等关键内容。通过实战案例(如企业级项目自动化测试、持续交付流程搭建),帮助学习者掌握 Java 项目质量保障与自动化交付的完整体系。

2025.10.24

926

26

Vibeknow在线使用入口合集
Vibeknow在线使用入口合集

本专题汇总了Vibeknow在线创作视频的官方入口及网页版使用教程,涵盖PPT、PDF、Word等文档一键转讲解视频的核心操作,并整理了免费版水印规则与手机端浏览器访问指南,助你快速将知识内容视频化。

2026.09.21

0

20

NumPy随机数文件读写与dtype数据类型
NumPy随机数文件读写与dtype数据类型

本专题整理 NumPy 随机数、文件读写与 dtype 数据类型相关教程,覆盖 Generator/random、随机数种子、正态分布采样、npy/npz/CSV/TXT 保存读取、loadtxt/savetxt、memmap、大文件处理、astype 类型转换、结构化 dtype、整数溢出和精度丢失等场景。

2026.09.21

0

24

NumPy矩阵运算与线性代数计算
NumPy矩阵运算与线性代数计算

本专题整理 NumPy 矩阵运算与线性代数计算相关教程,覆盖矩阵乘法、dot 与 @ 运算符、逆矩阵、行列式、特征值与特征向量、SVD、线性方程组、欧氏距离、矩阵分解和大规模矩阵性能优化等内容,帮助读者掌握 np.linalg 与矩阵计算实战。

2026.09.21

0

20

NumPy广播机制数学运算与统计分析
NumPy广播机制数学运算与统计分析

本专题整理 NumPy 广播机制、数组数学运算与统计分析相关教程,覆盖广播规则、维度对齐、矩阵与数组加减除法、向量化计算、均值方差、分位数、中位数、直方图和 unique 频次统计等场景,帮助读者掌握 ndarray 高效计算与统计处理方法。

2026.09.21

0

17

NumPy数组创建索引切片与数据选择
NumPy数组创建索引切片与数据选择

本专题整理 NumPy 数组创建、索引、切片与数据选择相关教程,覆盖 np.array、zeros/ones、多维数组形状、基础切片、花式索引、布尔索引、条件筛选、视图与副本等常用场景,帮助读者系统掌握 ndarray 数据构造与高效提取方法。

2026.09.21

0

12

Aionclaw智能助手介绍
Aionclaw智能助手介绍

本专题汇总了AionClaw(AI龙虾助手)的功能介绍与在线使用入口。AionClaw是杭州趣猿人工智能有限公司推出的桌面级AI智能体,能直接在电脑上读写文件、运行脚本、操作浏览器,自动交付Word、PPT、Excel等成品。

2026.09.20

20

13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
FastAPI官方教程文档
FastAPI官方教程文档

共0课时 | 0人学习

Iris框架官方文档
Iris框架官方文档

共0课时 | 0人学习