如何在不重构的情况下用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
相关产品推荐
相关产品推荐

