场景
在一次开发过程中遇到了需要mock合作方接口,但是使用了常用的mock方式却出现了异常。
问题
但在使用jmockit的@Mocked模拟方式时总报错,如下:
java.lang.IllegalArgumentException: Invalid type for partial mocking: interface com.test.common.gateway.PanterGatewayService
at com.test.common.gateway.TestMockTest$1.<init>(TestMockTest.java:32)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
实现代码如下:
public class BizMockTest extends SpringTestCase {
@Resource
private GatewayService gatewayService;
@Before
public void before() {
new Expectations(gatewayService) {
{
gatewayService.queryLimit((CommonRequest<LimitQueryRequest>) any);
CommonResponse<LimitQueryResponse> response = new CommonResponse();
response.setCode("000000");
response.setDesc("SUCCESS");
response.setResponse(LimitQueryResponse.builder()
.sumAmt(BigDecimal.valueOf(100000))
.leftAmt(BigDecimal.valueOf(100000))
.quotaState("3")
.build());
result = response;
}
};
}
分析
以往mock代码如下均没问题,那这个又是为什么呢?对比之下发现原来 GatewayService 是在配置加载时@Configuration才初始化Bean,也就是运行时初始化,而且也没有实现类。
如下:
@Configuration
public class InitConfig {
@Bean("GatewayService")
public GatewayService gatewayService() {
return Feign.builder()
.client(new OkHttpClient())
.retryer(Retryer.NEVER_RETRY)
.logger(new Slf4jLogger())
.logLevel(Logger.Level.FULL)
.target(GatewayService.class);
}
解决方案
经过查阅jmockit的API找到了如下方式实现。
方式一:通过 @Capturing 注解实现;
public class Test extends SpringTestCase {
@Resource
private GatewayService gatewayService;
// 在单元测试放在中使用 @Capturing 可以实现mock接口,所有实现类的实例均被mock
@Test
public void testLoanInit(@Capturing final GatewayService gatewayService) {
new Expectations() {
{
gatewayService.queryLimit((CommonRequest) any);
CommonResponse response = new CommonResponse();
response.setCode("000000");
response.setDesc("SUCCESS");
response.setResponse(LimitQueryResponse.builder()
.sumAmt(BigDecimal.valueOf(100000))
.leftAmt(BigDecimal.valueOf(100000))
.quotaState("3")
.build());
result = response;
}
};
CommonResponse commonResponse = gatewayService.queryLimit(new CommonRequest());
// 输出:{"code":"000000","desc":"SUCCESS","response":{"leftAmt":100000,"quotaState":"3","sumAmt":100000}}
System.out.println(JSONObject.toJSONString(limitQueryCommonResponse));
// 调用方法
invokeTest();
}
public void invokeTest() {
CommonResponse commonResponse = gatewayService.queryLimit(new CommonRequest());
// 输出:{"code":"000000","desc":"SUCCESS","response":{"leftAmt":100000,"quotaState":"3","sumAmt":100000}}
System.out.println(JSONObject.toJSONString(limitQueryCommonResponse));
}
}
方式二:自己写一个假接口实现类,即可使用原来的方式,或者直接再mock实现类中实现模拟返回 ;
// 写一个mock实现类
@Service("gatewayService")
public class GatewayServiceMockImpl implements GatewayService {
@Override
public CommonResponse<LimitQueryResponse> queryLimit(CommonResponse<LimitQueryResponse> request) {
// mock 数据返回或使用 @Mocked 方式构造返回数据
return null;
}
}
有了一个接口实现,即可使用 @Mocked 方式 mock了;
public class BizMockTest extends SpringTestCase {
@Resource
private GatewayService gatewayService;
@Before
public void before() {
new Expectations(gatewayService) {
{
gatewayService.queryLimit((CommonRequest<LimitQueryRequest>) any);
CommonResponse<LimitQueryResponse> response = new CommonResponse();
response.setCode("000000");
response.setDesc("SUCCESS");
response.setResponse(LimitQueryResponse.builder()
.sumAmt(BigDecimal.valueOf(100000))
.leftAmt(BigDecimal.valueOf(100000))
.quotaState("3")
.build());
result = response;
}
};
}