場(chǎng)景
在一次開發(fā)過程中遇到了需要mock合作方接口,但是使用了常用的mock方式卻出現(xiàn)了異常尉剩。
問題
但在使用jmockit的@Mocked模擬方式時(shí)總報(bào)錯(cuò)努酸,如下:
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)
實(shí)現(xiàn)代碼如下:
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代碼如下均沒問題齿风,那這個(gè)又是為什么呢禾蚕?對(duì)比之下發(fā)現(xiàn)原來 GatewayService 是在配置加載時(shí)@Configuration才初始化Bean您朽,也就是運(yùn)行時(shí)初始化,而且也沒有實(shí)現(xiàn)類换淆。
如下:
@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);
}
解決方案
經(jīng)過查閱jmockit的API找到了如下方式實(shí)現(xiàn)哗总。
方式一:通過 @Capturing 注解實(shí)現(xiàn)几颜;
public class Test extends SpringTestCase {
@Resource
private GatewayService gatewayService;
// 在單元測(cè)試放在中使用 @Capturing 可以實(shí)現(xiàn)mock接口,所有實(shí)現(xiàn)類的實(shí)例均被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));
// 調(diào)用方法
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));
}
}
方式二:自己寫一個(gè)假接口實(shí)現(xiàn)類讯屈,即可使用原來的方式蛋哭,或者直接再mock實(shí)現(xiàn)類中實(shí)現(xiàn)模擬返回 ;
// 寫一個(gè)mock實(shí)現(xiàn)類
@Service("gatewayService")
public class GatewayServiceMockImpl implements GatewayService {
@Override
public CommonResponse<LimitQueryResponse> queryLimit(CommonResponse<LimitQueryResponse> request) {
// mock 數(shù)據(jù)返回或使用 @Mocked 方式構(gòu)造返回?cái)?shù)據(jù)
return null;
}
}
有了一個(gè)接口實(shí)現(xiàn)涮母,即可使用 @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;
}
};
}