背景:

在项目开发中,会有调用第三方接口的场景。当开发时,对方不愿意提供测试服务器给我们调用,或者有的接口会按调用次数进行计费。当联调时,第三方的测试服务器也可能会出现不稳定,如果他们的服务挂了,我们就一直等着服务恢复,那么这就相当影响效率了。如果我们在开发时,就定义一个挡板或者mock服务,在发起调用时,不直接调到第三方接口,而是调到我们自己的挡板代码或者mock服务,这样就可以避免这些问题了。

优势:

  • 挡板代码,不需要侵入业务代码,可以根据入参做一些动态结果返回
  • 不需要专门开发一个挡板服务,并且在每次启动客户端都先启动挡板服务
  • 可以自由选择使用挡板还是Mock数据

Demo详细代码,已经提交到Github,欢迎star

Demo地址: https://github.com/Seifon/FeignStubMock


一、下面我就以一个第三方SMS短信接口来做演示:

首先,我们写一个Feign客户端接口,正常调用第三方接口:

1.定义一个SMS短信的Feign客户端接口:
import cn.seifon.example.feignstubmock.dto.YunxunSmsReqDto;
import cn.seifon.example.feignstubmock.dto.YunxunSmsRespDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; /**
* @Author: Seifon
* @Description:
* @Date: Created in 10:24 2019/1/7
*/
@FeignClient(name = "smsclient", url = "${sms.url}", primary = false)
public interface YunxunSmsFeign { /**
*
* @param request
* @return {"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}
* @return {"code":"107","msgId":"","time":"20190125162358","errorMsg":"手机号码格式错误"}
*/
@PostMapping("/msg/variable/json")
YunxunSmsRespDto send(@RequestBody YunxunSmsReqDto request);
}

注意:@FeignClient注解里面的primary属性一定要设置为false,这是为了防止在开启Feign挡板时,出现多个Feign客户端导致启动报错。

2.写一个单元测试:
import cn.seifon.example.feignstubmock.dto.YunxunSmsReqDto;
import cn.seifon.example.feignstubmock.dto.YunxunSmsRespDto;
import cn.seifon.example.feignstubmock.feign.YunxunSmsFeign;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class FeignStubMockApplicationTests {
@Autowired
private YunxunSmsFeign yunxunSmsFeign; @Test
public void feignStubMockTest() {
YunxunSmsReqDto yunxunSmsReqDto=new YunxunSmsReqDto();
yunxunSmsReqDto.setAccount("XXXXXXX");
yunxunSmsReqDto.setPassword("XXXXXXX");
yunxunSmsReqDto.setMsg("登录验证码:{$var},请不要对非本人透露。");
yunxunSmsReqDto.setParams("13011112222,123456");
yunxunSmsReqDto.setReport("true"); YunxunSmsRespDto send = yunxunSmsFeign.send(yunxunSmsReqDto); //打印结果
System.out.println(JSON.toJSON(send));
} }
3.1.我们输入一个正确的手机号,拿一个成功的结果:
2019-01-28 11:17:56.718 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] ---> POST http://smssh1.253.com/msg/variable/json HTTP/1.1
2019-01-28 11:17:56.719 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] Content-Type: application/json;charset=UTF-8
2019-01-28 11:17:56.720 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] Content-Length: 160
2019-01-28 11:17:56.720 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send]
2019-01-28 11:17:56.721 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] {"account":"XXXXXX","password":"XXXXXXX","msg":"登录验证码:{$var},请不要对非本人透露。","params":"17311112222,123456","report":"true"}
2019-01-28 11:17:56.721 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] ---> END HTTP (160-byte body)
2019-01-28 11:17:56.958 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] <--- HTTP/1.1 200 OK (236ms)
2019-01-28 11:17:56.960 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] connection: keep-alive
2019-01-28 11:17:56.962 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] content-length: 109
2019-01-28 11:17:56.963 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] content-type: application/json;charset=UTF-8
2019-01-28 11:17:56.965 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] date: Mon, 28 Jan 2019 03:17:56 GMT
2019-01-28 11:17:56.966 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send]
2019-01-28 11:17:56.971 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] {"code":"0","failNum":"0","successNum":"1","msgId":"19012811175621982","time":"20190128111756","errorMsg":""}
2019-01-28 11:17:56.972 DEBUG 6920 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] <--- END HTTP (109-byte body)
{"code":"0","failNum":"0","successNum":"1","msgId":"19012811175621982","time":"20190128111756","errorMsg":""}

此时,我们可以根据日志,看到请求的地址也是第三方的url

3.2.我们输入一个错误的手机号,拿一个失败的结果:
2019-01-28 11:21:15.300 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] ---> POST http://smssh1.253.com/msg/variable/json HTTP/1.1
2019-01-28 11:21:15.301 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] Content-Type: application/json;charset=UTF-8
2019-01-28 11:21:15.302 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] Content-Length: 152
2019-01-28 11:21:15.302 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send]
2019-01-28 11:21:15.303 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] {"account":"XXXXX","password":"XXXXXXX","msg":"登录验证码:{$var},请不要对非本人透露。","params":"173,123456","report":"true"}
2019-01-28 11:21:15.303 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] ---> END HTTP (152-byte body)
2019-01-28 11:21:15.470 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] <--- HTTP/1.1 200 OK (165ms)
2019-01-28 11:21:15.471 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] connection: keep-alive
2019-01-28 11:21:15.473 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] content-length: 87
2019-01-28 11:21:15.474 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] content-type: application/json;charset=UTF-8
2019-01-28 11:21:15.476 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] date: Mon, 28 Jan 2019 03:21:15 GMT
2019-01-28 11:21:15.477 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send]
2019-01-28 11:21:15.483 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] {"code":"107","msgId":"","time":"20190128112115","errorMsg":"手机号码格式错误"}
2019-01-28 11:21:15.484 DEBUG 5288 --- [ main] c.s.e.f.feign.YunxunSmsFeign : [YunxunSmsFeign#send] <--- END HTTP (87-byte body)
{"code":"107","msgId":"","time":"20190128112115","errorMsg":"手机号码格式错误"}

当我们知道了两种情况下出现的结果,那么我们就可以模拟响应结果啦。小技巧:我们可以先跟对方调接口,把各种响应报文保存下来,方便后面直接mock数据


二、接下来进入挡板编写环节:

1.编写一个YunxunSmsFeignStub类,并实现YunxunSmsFeign接口:
import cn.seifon.example.feignstubmock.dto.YunxunSmsReqDto;
import cn.seifon.example.feignstubmock.dto.YunxunSmsRespDto;
import cn.seifon.example.feignstubmock.feign.YunxunSmsFeign;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component; import java.util.Date; /**
* @Author: Seifon
* @Description:
* @Date: Created in 10:24 2019/1/7
*/
@Primary //注意:需要在原Feign接口@FeignClient注解加入primary = false 属性
@Component
@ConditionalOnProperty(name = "feign-stub.yunxun.sms.mode", havingValue = "stub")
public class YunxunSmsFeignStub implements YunxunSmsFeign {
private static final Logger LOG = LoggerFactory.getLogger(YunxunSmsFeignStub.class); @Override
public YunxunSmsRespDto send(YunxunSmsReqDto request) {
YunxunSmsRespDto yunxunSmsRespDto = new YunxunSmsRespDto(); //模拟正常响应结果
yunxunSmsRespDto.setCode("0");
yunxunSmsRespDto.setFailNum("0");
yunxunSmsRespDto.setSuccessNum("1");
yunxunSmsRespDto.setMsgId(String.valueOf(RandomUtils.nextLong(19000000000000000L, 19999999999999999L)));
yunxunSmsRespDto.setTime(DateFormatUtils.format(new Date(), "yyyyMMddHHmmss"));
yunxunSmsRespDto.setErrorMsg(""); String params = request.getParams();
String[] paramSplit = StringUtils.split(params, ",");
if (paramSplit[0].length() != 11) {
//模拟错误响应结果
yunxunSmsRespDto.setCode("107");
yunxunSmsRespDto.setMsgId("");
yunxunSmsRespDto.setErrorMsg("手机号码格式错误");
}
return yunxunSmsRespDto;
}
}

注意:必须标注@Primary注解,否则启动会报错。@ConditionalOnProperty的作用就是根据application.yaml配置的相关属性,判断是否注入Spring容器

2.application.yaml文件,加入下面的配置:
sms:
url: 'http://smssh1.253.com' #yunxun:代表第三方系统名称,sms:代表业务名称,mode:代表Stub模式,url:代表mock服务地址
feign-stub:
yunxun:
sms:
mode: 'stub'
3.为了区分返回的内容是挡板结果,我们可以写一个AOP切面打印日志:
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; /**
* @Author: Seifon
* @Description:
* @Date: Created in 10:24 2019/1/7
*/
@Aspect
@Component
public class FeignStubAspect { private static final Logger LOG = LoggerFactory.getLogger(FeignStubAspect.class); @Pointcut("execution(* cn.seifon.example.feignstubmock..stub.*.*(..))")
public void pointCut(){} @Around("pointCut()")
public Object around(ProceedingJoinPoint pjp){
String name = StringUtils.join(pjp.getTarget().getClass().getName(), ".", pjp.getSignature().getName());
LOG.info("-----【{}】---- 进入挡板模式... request: 【{}】", name, JSON.toJSON(pjp.getArgs()));
try {
Object proceed = pjp.proceed();
LOG.info("-----【{}】---- 退出挡板模式... request: 【{}】, response: 【{}】", name, JSON.toJSON(pjp.getArgs()), JSON.toJSON(proceed));
return proceed;
} catch (Throwable e) {
e.printStackTrace();
} return null;
} }
4.1.运行之前写的单元测试代码(输入一个正确的手机号):
2019-01-28 11:32:51.255  INFO 7488 --- [           main] c.s.e.f.aspect.FeignStubAspect           : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 进入挡板模式... request: 【[{"msg":"登录验证码:{$var},请不要对非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】
2019-01-28 11:32:51.975 INFO 7488 --- [ main] c.s.e.f.aspect.FeignStubAspect : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 退出挡板模式... request: 【[{"msg":"登录验证码:{$var},请不要对非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】, response: 【{"code":"0","failNum":"0","successNum":"1","msgId":"19148964234899564","time":"20190128113251","errorMsg":""}】
{"code":"0","failNum":"0","successNum":"1","msgId":"19148964234899564","time":"20190128113251","errorMsg":""}
4.2.运行之前写的单元测试代码(输入一个错误的手机号):
2019-01-28 11:35:27.177  INFO 15204 --- [           main] c.s.e.f.aspect.FeignStubAspect           : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 进入挡板模式... request: 【[{"msg":"登录验证码:{$var},请不要对非本人透露。","password":"XXXXXXX","report":"true","params":"130,123456","account":"XXXXXXX"}]】
2019-01-28 11:35:27.900 INFO 15204 --- [ main] c.s.e.f.aspect.FeignStubAspect : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 退出挡板模式... request: 【[{"msg":"登录验证码:{$var},请不要对非本人透露。","password":"XXXXXXX","report":"true","params":"130,123456","account":"XXXXXXX"}]】, response: 【{"code":"107","failNum":"0","successNum":"1","msgId":"","time":"20190128113527","errorMsg":"手机号码格式错误"}】
{"code":"107","failNum":"0","successNum":"1","msgId":"","time":"20190128113527","errorMsg":"手机号码格式错误"}

以上代码就完成了一个stub挡板功能,可有时候,我们已经拿到第三方接口的返回报文,并切不想去写一大段Stub代码。那么这个时候,我们就可以选择下面的Mock方式去完成我们的功能。


三、接下来进入Mock环节:

1. 首先准备一个mock服务,这里我就用自己比较喜欢的一个mock工具(mock-json-server)给大家演示:
1.1 安装nodejs:
参看官网:http://nodejs.cn/
1.2 安装mock-json-server:
npm install -g mock-json-server
1.3 新建mock数据文件(命名为:data.json):
{
"/msg/variable/json": {
"post": {
"code":"0",
"failNum":"0",
"successNum":"1",
"msgId":"19012516213625881",
"time":"20190125162136",
"errorMsg":""
}
}
}
1.4 运行:
mock-json-server {path}/data.json --port=1240

{path}替换为存放data.json的绝对路径
1.5 如果显示如下结果,就代表mock服务运行成功:
JSON Server running at http://localhost:1240/

mock-json-server具体使用文档,请参考:https://www.npmjs.com/package/mock-json-server

2. 准备工作做好后,接下来,就进入Mock正式环节:
2.1 首先,我们定义一个YunxunSmsFeignMock接口,并且继承YunxunSmsFeign接口
import cn.seifon.example.feignstubmock.feign.YunxunSmsFeign;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component; /**
* @Author: Seifon
* @Description:
* @Date: Created in 10:24 2019/1/7
*/
@Primary //注意:需要在原Feign接口@FeignClient注解加入primary = false 属性
@Component
@ConditionalOnProperty(name = "feign-stub.yunxun.sms.mode", havingValue = "mock")
@FeignClient(name = "smsclient-mock", url = "${feign-stub.yunxun.sms.mockUrl}" ,path = "/")
public interface YunxunSmsFeignMock extends YunxunSmsFeign { }

注意:必须标注@Primary注解,否则启动时会报错。@FeignClient里的name属性不能跟原Feign接口名称相同,如果相同会启动报错。@ConditionalOnProperty的作用就是根据application.yaml配置的相关属性,判断是否注入Spring容器

2.2 application.yaml文件,加入下面的配置:
sms:
url: 'http://smssh1.253.com' #生产环境请勿添加此配置。mode说明:''-不开启, 'mock'-mock模式, 'stub'-stub模式。url说明:只有mock模式需要配置调试url。fund为第三方机构,repayment是业务名称
#yunxun:代表第三方系统名称,sms:代表业务名称,mode:代表挡板模式,url:代表mock服务地址
feign-stub:
yunxun:
sms:
mode: 'mock'
mockUrl: "http://localhost:1240"
2.3 为了区分返回的内容是Mock结果,我们可以写一个AOP切面打印日志:
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; /**
* @Author: Seifon
* @Description:
* @Date: Created in 10:24 2019/1/7
*/
@Aspect
@Component
public class FeignMockAspect { private static final Logger LOG = LoggerFactory.getLogger(FeignMockAspect.class); @Pointcut("execution(* cn.seifon.example.feignstubmock..mock.*.*(..))")
public void pointCut(){} @Around("pointCut()")
public Object around(ProceedingJoinPoint pjp){
String name = StringUtils.join(pjp.getTarget().getClass().getName(), ".", pjp.getSignature().getName());
LOG.info("-----【{}】---- 进入Mock模式... request: 【{}】", name, JSON.toJSON(pjp.getArgs()));
try {
Object proceed = pjp.proceed();
LOG.info("-----【{}】---- 退出Mock模式... request: 【{}】, response: 【{}】", name, JSON.toJSON(pjp.getArgs()), JSON.toJSON(proceed));
return proceed;
} catch (Throwable e) {
e.printStackTrace();
} return null;
} }
2.4 运行之前的单元测试类,得到如下结果:
2019-01-28 16:16:35.567  INFO 8976 --- [           main] c.s.e.f.aspect.FeignMockAspect           : -----【com.sun.proxy.$Proxy95.send】---- 进入Mock模式... request: 【[{"msg":"登录验证码:{$var},请不要对非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】
2019-01-28 16:16:35.934 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] ---> POST http://localhost:1240/msg/variable/json HTTP/1.1
2019-01-28 16:16:35.935 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] Content-Type: application/json;charset=UTF-8
2019-01-28 16:16:35.936 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] Content-Length: 152
2019-01-28 16:16:35.936 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send]
2019-01-28 16:16:35.937 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] {"account":"XXXXXXX","password":"XXXXXXX","msg":"登录验证码:{$var},请不要对非本人透露。","params":"13011112222,123456","report":"true"}
2019-01-28 16:16:35.937 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] ---> END HTTP (152-byte body)
2019-01-28 16:16:36.021 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] <--- HTTP/1.1 200 OK (82ms)
2019-01-28 16:16:36.021 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] access-control-allow-origin: *
2019-01-28 16:16:36.022 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] connection: keep-alive
2019-01-28 16:16:36.023 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] content-length: 109
2019-01-28 16:16:36.023 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] content-type: application/json; charset=utf-8
2019-01-28 16:16:36.024 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] date: Mon, 28 Jan 2019 08:16:36 GMT
2019-01-28 16:16:36.024 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] etag: W/"6d-XqhLoZB8r6IRF2Lb6CWoIVVNhIQ"
2019-01-28 16:16:36.025 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] x-content-type-options: nosniff
2019-01-28 16:16:36.026 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] x-powered-by: Express
2019-01-28 16:16:36.027 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send]
2019-01-28 16:16:36.030 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] {"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}
2019-01-28 16:16:36.030 DEBUG 8976 --- [ main] c.s.e.f.feign.mock.YunxunSmsFeignMock : [YunxunSmsFeignMock#send] <--- END HTTP (109-byte body)
2019-01-28 16:16:36.227 INFO 8976 --- [ main] c.s.e.f.aspect.FeignMockAspect : -----【com.sun.proxy.$Proxy95.send】---- 退出Mock模式... request: 【[{"msg":"登录验证码:{$var},请不要对非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】, response: 【{"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}】
{"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}

说明:此时我们根据日志,会发现feign调用的url已经变为我们的Mock服务地址了。同理,如果要返回失败结果,只需要修改data.json文件,再次调用后,即可得到我们想要的结果了。


四、结语:

如果有什么需要改进的地方,或者不正确的地方,请在评论里面提出并指正。谢谢!

Demo详细代码,已经提交到Github,欢迎star

Demo地址: https://github.com/Seifon/FeignStubMock

项目结构,如图:

原文地址:http://www.seifon.cn/2019/01/28/Feign-Stub挡板和Mock/

Feign挡板和Mock的更多相关文章

  1. vue+vux-ui+axios+mock搭建一个简单vue框架

    1.首先感谢同事 2.之前一直在做angularjs的项目,目前vue火热,所以自己搭建了一个的vue框架,在此作为记录 vue+vux-ui这里就不介绍了,有很多博客都写的很详细了. 下面简单记录下 ...

  2. 对Feign的请求url 重写

    需求:对当前请求的 url 重新构建 debug feign 的执行可知,重写 LoadBalancerFeignClient 类中的 execute 方法即可控制当前请求的url 代码分析 当引入  ...

  3. 跟着小程学微服务-Mock自动化系统的原理及实现

    一.前言 在之前的文章 http://blog.csdn.net/u013970991/article/details/54862772 中已经介绍了"自动化Mock系统0.9版本" ...

  4. 换种思路写Mock,让单元测试更简单

    开篇引入 单元测试中的Mock方法,通常是为了绕开那些依赖外部资源或无关功能的方法调用,使得测试重点能够集中在需要验证和保障的代码逻辑上.在定义Mock方法时,开发者真正关心的只有一件事:" ...

  5. 单元测试:单元测试中的mock

    公司要求提升单元测试的质量,提高代码的分支覆盖率和行覆盖率,安排我研究单元测试,指定方案分享并在开发部普及开.整理完资料后,同步一下到博客. 单元测试中的mock的目的 mock的主要目的是让单元测试 ...

  6. Feign远程调用

    有关微服务中,服务与服务如何通信,我已经给大家介绍了Ribbon远程调用的相关知识,不知道大家有没有发现Ribbon的问题呢? Ribbon的问题 在Ribbon中,如果我们想要发起一个调用,是这样的 ...

  7. Pramp mock interview (4th practice): Matrix Spiral Print

    March 16, 2016 Problem statement:Given a 2D array (matrix) named M, print all items of M in a spiral ...

  8. Google C++单元测试框架GoogleTest---Google Mock简介--概念及基础语法

    就在昨天终于做了gtest的分享,我的预研工作终于结束了,感觉离我辞职的日子不远了,毕竟是专注java二百年啊,要告别实习啦.. 这篇是GoogleMock的简介文档,会在后边附带一个自己的例子. 一 ...

  9. Pramp - mock interview experience

    Pramp - mock interview experience   February 23, 2016 Read the article today from hackerRank blog on ...

随机推荐

  1. Qt自定义委托在QTableView中绘制控件、图片、文字(内容比较全)

    自定义委托,继承于,QStyledItemDelegate类,重载Paint()函数, 1.实现在QTableView中绘制 格式字符串 2.实现在QTableView中绘制进度条 3.实现在QTab ...

  2. C语言宏定义##连接符和#符的使用(MFC就是靠##自动把消息和消息函数对应起来了,借助宏来减少switch case代码的编写量)

    C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念).下面对常遇到的宏的使用问题做了简单总结. 关于#和## 在C语言的宏中,#的功能是将其后面 ...

  3. Windows完成端口编程

    Windows完成端口编程目录一 基本概念二 OVERLAPPED数据结构三 完成端口的内部机制创建完成端口完成端口线程的工作原理线程间数据传递线程的安全退出 一 基本概念       设备---wi ...

  4. 发布Qt Quick桌面应用程序的方法(使得planets在XP上运行)

    发布Qt Quick桌面应用程序的方法 Qt是一款优秀的跨平台开发框架,它可以在桌面.移动平台以及嵌入式平台上运行.目前Qt 5介绍程序发布的文章帖子比较少.大家又非常想要知道如何发布Qt应用程序,于 ...

  5. 梭子鱼VS多备份 虽殊途却同归

    备份,对于企业来说,不仅是一个已经拥有多年历史的传统IT工作,还关系着企业自身的生死存亡.在云计算时代下,备份业务成为企业的必选项,已经成为云计算服务最为热门的领域之一.基于云的备份正在深刻改变着备份 ...

  6. 【算法随记三】小半径中值模糊的急速实现(16MB图7.5ms实现) + Photoshop中蒙尘和划痕算法解读。

    在本人的博客里,分享了有关中值模糊的O(1)算法,详见:任意半径中值滤波(扩展至百分比滤波器)O(1)时间复杂度算法的原理.实现及效果 ,这里的算法的执行时间和参数是无关的.整体来说,虽然速度也很快, ...

  7. ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理

    我们了解ABP框架内部自动记录审计日志和登录日志的,但是这些信息只是在相关的内部接口里面进行记录,并没有一个管理界面供我们了解,但是其系统数据库记录了这些数据信息,我们可以为它们设计一个查看和导出这些 ...

  8. System.arraycopy 和 Arrays.copyOf

    System.arraycopy /* native关键字 本地方法 System类 java.lang.System.class 参数说明: src - 源数组. srcPos - 源数组中的起始位 ...

  9. Java 中无返回值的方法在使用时应该注意的问题

    Java 中的方法是形态多样的.无返回值的方法在使用时应该规避哪些问题呢? 一.不可以打印调用或是赋值调用,只能是单独调用(非常重要): 二.返回值没有,不代表参数就没有: 三.不能return一个具 ...

  10. CSS3之响应式布局及其应用

    使用 Media 属性前需添加兼容移动设备优先代码 <meta name=”viewport”content=”width=device-width,initial-scale=1.0,maxi ...