求助:Mockito无法Mock Final类方法的替代解决方案
问题描述
我需要对服务中这段代码的execute()、context()和build()方法做Mock:
Attributes attributes = targetClient.getAttributes( TargetDeliveryRequest.builder() .execute(new ExecuteRequest().mboxes(Collections.singletonList(mboxRequest))) .context(new Context().channel(ChannelType.WEB)).build());
TargetDeliveryRequest是第三方库类,无法修改,核心定义如下:
public class TargetDeliveryRequest { .... public static TargetDeliveryRequestBuilder builder() { return new TargetDeliveryRequestBuilder(); } }
我尝试MockTargetDeliveryRequestBuilder:
TargetDeliveryRequestBuilder targetDeliveryRequestBuilder = mock(TargetDeliveryRequestBuilder.class);
但该类是final类,测试时抛出错误:
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class com.adobe.target.edge.client.model.TargetDeliveryRequestBuilder
Mockito cannot mock/spy because : - final class
TargetDeliveryRequestBuilder的关键代码:
public final class TargetDeliveryRequestBuilder { ... TargetDeliveryRequestBuilder() { } ... public TargetDeliveryRequestBuilder context(Context context) { this.request.context(context); return this; } public TargetDeliveryRequestBuilder execute(ExecuteRequest execute) { this.request.execute(execute); return this; } ... public TargetDeliveryRequest build() { this.setTargetValues(); this.setVisitorValues(); this.createVisitorId(); this.setExperienceCloudValues(); TargetDeliveryRequest targetDeliveryRequest = TargetDeliveryRequest.fromRequest(this.request); targetDeliveryRequest.setLocationHint(this.locationHint); targetDeliveryRequest.setSessionId(this.sessionId); targetDeliveryRequest.setVisitor(this.visitor); targetDeliveryRequest.setDecisioningMethod(this.decisioningMethod); return targetDeliveryRequest; } ... }
已知添加mockito-inline可解决问题,但项目版本较旧,引入后会破坏大量现有测试,因此该方案不可行。
环境版本:
- Java: 8
- mockito-core: 3.12.4
- powermock-module-junit: 2.0.9
- powermock-api-mockito2: 2.0.9
可行解决方案
利用项目已引入的PowerMock依赖,可通过以下步骤实现Mock:
- 配置测试类注解
在测试类上添加PowerMock的Runner注解,并指定需要处理的类:
@RunWith(PowerMockRunner.class) @PrepareForTest({TargetDeliveryRequest.class, TargetDeliveryRequestBuilder.class}) public class YourServiceTest { // 测试逻辑 }
- Mock静态builder方法
用PowerMock拦截TargetDeliveryRequest.builder()的静态调用,返回Mock的builder实例:
// 静态Mock TargetDeliveryRequest类 mockStatic(TargetDeliveryRequest.class); TargetDeliveryRequestBuilder mockBuilder = mock(TargetDeliveryRequestBuilder.class); // 指定builder()返回Mock实例 when(TargetDeliveryRequest.builder()).thenReturn(mockBuilder);
- Mock链式调用方法
由于builder采用链式调用(方法返回自身),需要依次Mockexecute()、context()返回自身,再Mockbuild()返回目标对象:
// Mock execute方法,返回builder自身 when(mockBuilder.execute(any(ExecuteRequest.class))).thenReturn(mockBuilder); // Mock context方法,返回builder自身 when(mockBuilder.context(any(Context.class))).thenReturn(mockBuilder); // Mock build方法,返回自定义的TargetDeliveryRequest实例(也可以Mock这个实例) TargetDeliveryRequest mockDeliveryRequest = mock(TargetDeliveryRequest.class); when(mockBuilder.build()).thenReturn(mockDeliveryRequest);
- Mock getAttributes方法
最后MocktargetClient.getAttributes()的返回值,得到你需要的Attributes实例:
TargetClient mockTargetClient = mock(TargetClient.class); Attributes mockAttributes = mock(Attributes.class); when(mockTargetClient.getAttributes(mockDeliveryRequest)).thenReturn(mockAttributes);
通过这套配置,就能完整Mock这段链式调用逻辑,无需引入mockito-inline,不会影响现有测试。
内容的提问来源于stack exchange,提问作者Gustavo Rey
相关产品推荐
相关产品推荐

