You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不重构的情况下用Mockito模拟遗留代码中的RemoteService对象?

如何在不重构遗留代码的前提下用Mockito模拟approvalAction中的RemoteService?

我正在使用Mockito框架编写单元测试,现有如下遗留代码,请问如何在不重构代码的前提下,在approvalAction方法中模拟RemoteService客户端对象?

代码如下:

public Map<String, String> approvalAction(long documentId, ActionCommandDTO request, FormData formData, byte[] prevData) { 
    RemoteService client = getRemoteService(); 
    String urlString = String.format("formExtensions/%s?%s", formData.getId(), getAuthParam(formData.getRealm())); 
    try { 
        response = client.postEntity(urlString, String.class, approvalSvcRequestStr); 
    } catch (Exception e) { 
        // TODO: handle rollback properly for P2P 
        handleApprovalActionFailed(documentId, request, formData, prevData); 
    } 
    return map; 
} 
private RemoteService getRemoteService() { 
    RemoteServiceConfig remoteServiceConfig = (RemoteServiceConfig) this.serviceConfigRegistry.getServiceConfigs().get("approval"); 
    remoteServiceConfig.setClientID(clientId); 
    remoteServiceConfig.setClientSecret(privateSecret); 
    RemoteService remoteService = new RemoteService(remoteServiceConfig, authorizationHeaderServiceImpl); 
    return remoteService; 
}

要在不重构代码的情况下模拟RemoteService,核心是拦截私有方法getRemoteService()的调用,让它返回我们预先创建的mock对象。这里需要用到PowerMockito(它是Mockito的扩展,支持mock私有方法、静态方法和构造方法等场景),具体实现步骤如下:

1. 准备依赖

确保你的测试依赖中包含PowerMockito与Mockito的适配包(以JUnit 4为例,需要引入powermock-module-junit4和powermock-api-mockito2;JUnit 5则对应powermock-module-junit5)。

2. 编写测试用例

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.Map;

@RunWith(PowerMockRunner.class)
@PrepareForTest(YourServiceClass.class) // 替换为包含approvalAction方法的实际类名
public class YourServiceClassTest {

    @Mock
    private RemoteService mockRemoteService;

    private YourServiceClass serviceUnderTest;

    @Test
    public void testApprovalAction_Success() throws Exception {
        // 1. 创建被测类的spy实例——保留原有逻辑,但允许拦截私有方法调用
        serviceUnderTest = PowerMockito.spy(new YourServiceClass());

        // 2. 拦截私有方法getRemoteService,让它返回我们的mock对象
        PowerMockito.when(serviceUnderTest, "getRemoteService").thenReturn(mockRemoteService);

        // 3. 模拟postEntity方法的成功返回值(根据业务需求调整)
        String mockSuccessResponse = "approval_passed";
        PowerMockito.when(mockRemoteService.postEntity(
                PowerMockito.anyString(),
                PowerMockito.eq(String.class),
                PowerMockito.anyString()
        )).thenReturn(mockSuccessResponse);

        // 4. 调用被测方法
        Map<String, String> result = serviceUnderTest.approvalAction(
                1001L,
                new ActionCommandDTO(),
                new FormData(),
                new byte[]{}
        );

        // 5. 验证行为与结果
        PowerMockito.verify(mockRemoteService).postEntity(
                PowerMockito.anyString(),
                PowerMockito.eq(String.class),
                PowerMockito.anyString()
        );
        // 可添加对返回结果result的业务断言,比如断言包含预期状态码
    }

    @Test
    public void testApprovalAction_Failure() throws Exception {
        serviceUnderTest = PowerMockito.spy(new YourServiceClass());
        PowerMockito.when(serviceUnderTest, "getRemoteService").thenReturn(mockRemoteService);

        // 模拟postEntity调用抛出异常
        RuntimeException mockRemoteException = new RuntimeException("Remote service timeout");
        PowerMockito.when(mockRemoteService.postEntity(
                PowerMockito.anyString(),
                PowerMockito.eq(String.class),
                PowerMockito.anyString()
        )).thenThrow(mockRemoteException);

        // 调用被测方法
        Map<String, String> result = serviceUnderTest.approvalAction(
                1001L,
                new ActionCommandDTO(),
                new FormData(),
                new byte[]{}
        );

        // 验证失败处理方法被调用(若handleApprovalActionFailed为public,否则需额外处理私有方法验证)
        PowerMockito.verify(serviceUnderTest).handleApprovalActionFailed(
                1001L,
                new ActionCommandDTO(),
                new FormData(),
                new byte[]{}
        );
    }
}

关键逻辑说明

  • @RunWith(PowerMockRunner.class):指定用PowerMock的测试运行器执行测试,确保能处理私有方法的拦截逻辑。
  • @PrepareForTest(YourServiceClass.class):告知PowerMock需要修改该类的字节码,从而实现私有方法的mock。
  • PowerMockito.spy():创建被测类的spy实例,既保留原有方法的正常逻辑,又允许我们针对性mock特定私有方法。
  • PowerMockito.when(serviceUnderTest, "getRemoteService").thenReturn(mockRemoteService):拦截getRemoteService私有方法的调用,直接返回我们预先创建的mock对象,这样approvalAction中拿到的client就是我们的mock实例,而非实际new出来的真实对象。

内容的提问来源于stack exchange,提问作者Sharan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:58:19