讲师中心 微信公众号
AI工具推荐 视频效率加速

Java中测试私有方法内部创建的对象:使用可注入工厂模式

夜强同学_5171

夜强同学_5171

发布时间:2025-11-22 18:33:02

|

187人浏览过

|

来源于php中文网

原创

Java中测试私有方法内部创建的对象:使用可注入工厂模式

当需要在java中测试一个公共方法,而该方法内部又调用了一个私有方法,且该私有方法通过`new`关键字创建了待测试对象时,直接使用传统mocking框架(如mockito)来模拟这个内部创建的对象是不可行的。本文将深入探讨这一挑战,并提供一种标准且推荐的解决方案:通过引入可注入的工厂模式来重构代码,从而实现对内部依赖的有效模拟和测试。

在Java单元测试中,我们经常使用Mocking框架(如Mockito)来隔离被测试单元与其依赖,以确保测试的独立性和专注性。然而,当一个对象在被测试类的一个私有方法内部通过new关键字直接实例化时,传统的Mocking技术会遇到瓶颈。这是因为Mocking框架通常通过代理或字节码修改来拦截对现有对象方法的调用,但它们无法干预方法内部的局部变量创建过程,特别是当这些对象是直接通过new操作符构造时。

考虑以下场景:一个ParentClass包含一个公共方法method1,它内部调用了一个私有方法privateMethod。privateMethod负责创建并返回一个ObjectNeeded2Mock的实例。

// 待测试的依赖对象
class ObjectNeeded2Mock {
    public String doSomething() {
        return "Real Value";
    }
    // 更多业务逻辑
}

// 私有方法内部创建依赖对象的类
class ParentClass {

    public ParentClass() {}

    public String method1(String argument) {
        // ... 其他逻辑
        Obj resultObj = privateMethod(argument); // 调用私有方法
        return "Processed: " + resultObj.getValue();
    }

    private Obj privateMethod(String argument) {
        // 问题所在:ObjectNeeded2Mock在这里被直接实例化
        ObjectNeeded2Mock obj = new ObjectNeeded2Mock();
        String processedValue = obj.doSomething() + " " + argument;
        return new Obj(processedValue);
    }
}

// 辅助类
class Obj {
    private String value;
    public Obj(String value) { this.value = value; }
    public String getValue() { return value; }
}

在这种结构下,如果我们想在测试method1时,模拟ObjectNeeded2Mock的行为,会发现难以实现。@InjectMock注解通常用于将Mock对象注入到被测试对象的字段中,但它无法改变私有方法内部的局部变量创建行为。

解决方案:引入可注入的工厂模式

解决此问题的最佳实践是重构代码,以允许对ObjectNeeded2Mock的创建过程进行控制。核心思想是:不要在privateMethod中直接new对象,而是将对象的创建职责委托给一个可注入的工厂。

立即学习Java免费学习笔记(深入)”;

步骤一:定义工厂接口和实现

Alibabacloud Sdk Client Initialization For Java
Alibabacloud Sdk Client Initialization For Java

在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。

下载

首先,为ObjectNeeded2Mock的创建定义一个工厂接口,并提供一个默认实现。

// 1. 定义一个工厂接口
interface ObjectNeeded2MockFactory {
    ObjectNeeded2Mock create();
}

// 2. 提供一个默认的工厂实现
class DefaultObjectNeeded2MockFactory implements ObjectNeeded2MockFactory {
    @Override
    public ObjectNeeded2Mock create() {
        return new ObjectNeeded2Mock(); // 实际创建对象
    }
}

步骤二:重构ParentClass以使用工厂

修改ParentClass,使其通过构造函数(或其他注入方式)接收ObjectNeeded2MockFactory的实例,并在privateMethod中使用该工厂来创建ObjectNeeded2Mock。

// 3. 重构ParentClass,通过构造函数注入工厂
class ParentClass {
    private final ObjectNeeded2MockFactory factory;

    // 构造函数注入工厂
    public ParentClass(ObjectNeeded2MockFactory factory) {
        this.factory = factory;
    }

    public String method1(String argument) {
        // ... 其他逻辑
        Obj resultObj = privateMethod(argument);
        return "Processed: " + resultObj.getValue();
    }

    private Obj privateMethod(String argument) {
        // 现在通过工厂创建ObjectNeeded2Mock
        ObjectNeeded2Mock obj = factory.create();
        String processedValue = obj.doSomething() + " " + argument;
        return new Obj(processedValue);
    }
}

步骤三:编写测试代码

现在,在测试ParentClass时,我们可以轻松地模拟ObjectNeeded2MockFactory,并控制它返回的ObjectNeeded2Mock实例。

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

class ParentClassTest {

    private ObjectNeeded2MockFactory mockFactory;
    private ObjectNeeded2Mock mockObjectNeeded2Mock;
    private ParentClass parentClass;

    @BeforeEach
    void setUp() {
        // 1. Mock工厂接口
        mockFactory = mock(ObjectNeeded2MockFactory.class);
        // 2. Mock ObjectNeeded2Mock实例
        mockObjectNeeded2Mock = mock(ObjectNeeded2Mock.class);

        // 3. 配置mockFactory,使其在调用create()时返回mockObjectNeeded2Mock
        when(mockFactory.create()).thenReturn(mockObjectNeeded2Mock);

        // 4. 使用mockFactory实例化ParentClass
        parentClass = new ParentClass(mockFactory);
    }

    @Test
    void testMethod1_withMockedDependency() {
        // Arrange
        String testArgument = "testArgument";
        String mockedResultFromObj = "Mocked Result";

        // 配置mockObjectNeeded2Mock的行为
        when(mockObjectNeeded2Mock.doSomething()).thenReturn(mockedResultFromObj);

        // Act
        String actualResult = parentClass.method1(testArgument);

        // Assert
        // 验证工厂的create方法是否被调用
        verify(mockFactory, times(1)).create();
        // 验证mockObjectNeeded2Mock的doSomething方法是否被调用
        verify(mockObjectNeeded2Mock, times(1)).doSomething();

        // 验证最终结果是否符合预期
        String expectedResult = "Processed: " + mockedResultFromObj + " " + testArgument;
        assertEquals(expectedResult, actualResult);
    }
}

注意事项与最佳实践

  1. 重构的价值: 尽管为了测试需要进行代码重构,但这种重构往往会带来更好的设计。它提高了代码的模块化程度、降低了耦合性,并使ParentClass的职责更加清晰(它不再负责ObjectNeeded2Mock的创建细节)。
  2. 私有方法的测试: 本文主要关注私有方法内部创建对象的测试。对于私有方法本身的逻辑,通常的建议是:如果私有方法逻辑复杂且难以通过公共方法间接测试,可能需要考虑其可见性(例如,提升为protected或包私有)或者将其提取为一个独立的、可测试的组件。但在大多数情况下,通过测试公共方法来覆盖私有方法的逻辑是足够的。
  3. 依赖注入框架: 在大型项目中,手动管理工厂的注入可能会变得繁琐。Spring、Guice或Dagger等依赖注入(DI)框架可以自动化这一过程,使依赖管理更加优雅和高效。
  4. 接口优于实现: 在定义工厂时,我们使用了接口ObjectNeeded2MockFactory。这是一种良好的实践,它增加了灵活性,允许在不修改ParentClass的情况下替换不同的工厂实现(例如,用于生产环境的默认工厂和用于测试的模拟工厂)。

总结

当Java私有方法内部通过new关键字创建了依赖对象时,直接对其进行Mocking是不可行的。解决此问题的标准方法是采用可注入的工厂模式。通过将对象的创建职责委托给一个工厂接口,并将其注入到主类中,我们可以在测试时轻松地模拟这个工厂,从而控制内部创建的依赖对象的行为。这种重构不仅解决了测试难题,也通常会提升代码的设计质量和可维护性。

热门AI工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

2111

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

397

2026.01.26

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3028

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

4209

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3649

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

366

2026.01.19

PHP 命令行脚本与自动化任务开发
PHP 命令行脚本与自动化任务开发

本专题系统讲解 PHP 在命令行环境(CLI)下的开发与应用,内容涵盖 PHP CLI 基础、参数解析、文件与目录操作、日志输出、异常处理,以及与 Linux 定时任务(Cron)的结合使用。通过实战示例,帮助开发者掌握使用 PHP 构建 自动化脚本、批处理工具与后台任务程序 的能力。

426

2025.12.13

Figma AI自动化智能数据填充与交互生成实战
Figma AI自动化智能数据填充与交互生成实战

告别机械重复,详细演示如何用 AI 填充业务真实数据,并自动为页面添加交互连线,将静态设计稿快速转变为动态原型。

245

2026.05.13

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

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

0

2026.09.21

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn