├── .gitignore ├── .travis.yml ├── README.md ├── img └── principle.jpg ├── pom.xml └── src ├── main ├── java │ └── com │ │ ├── alibaba │ │ └── dubbo │ │ │ ├── common │ │ │ └── Node.java │ │ │ └── rpc │ │ │ └── Invoker.java │ │ └── cmt │ │ └── des │ │ ├── EasyMockClusterWrapper.java │ │ ├── EasyMockHandler.java │ │ ├── EasyMockInvoker.java │ │ ├── MockConfig.java │ │ ├── MockValueResolver.java │ │ ├── PrimitiveWrapper.java │ │ ├── compatible │ │ ├── CompatibleEasyMockClusterWrapper.java │ │ ├── CompatibleEasyMockHandler.java │ │ ├── CompatibleEasyMockInvoker.java │ │ └── CompatibleResultAdaptor.java │ │ └── util │ │ ├── ClassHelper.java │ │ ├── ConfigUtils.java │ │ ├── HttpUtil.java │ │ └── ReflectUtils.java └── resources │ └── META-INF │ └── dubbo │ ├── com.alibaba.dubbo.rpc.cluster.Cluster │ └── org.apache.dubbo.rpc.cluster.Cluster └── test ├── java └── tests │ ├── HelloService.java │ ├── application │ └── EasyMockTest.groovy │ ├── component │ └── MockValueResolverTest.groovy │ └── model │ ├── BaseResponse.java │ ├── Person.java │ ├── QueryCurNeedRepayResponse.java │ └── Result.java └── resources └── mock.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | !.mvn/wrapper/* 5 | *.war 6 | *.zip 7 | *.tar 8 | *.tar.gz 9 | 10 | # eclipse ignore 11 | .settings/ 12 | .project 13 | .classpath 14 | 15 | # idea ignore 16 | .idea/ 17 | *.ipr 18 | *.iml 19 | *.iws 20 | 21 | # temp ignore 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | *.orig 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false # faster builds 3 | 4 | jdk: 5 | - openjdk8 6 | 7 | cache: 8 | directories: 9 | - $HOME/.m2 10 | 11 | install: skip 12 | 13 | script: 14 | - mvn --batch-mode clean install -DskipTests=false -Dmaven.javadoc.skip=true 15 | 16 | after_success: 17 | - bash <(curl -s https://codecov.io/bash) 18 | - echo "build success!" 19 | 20 | after_failure: 21 | - echo "build failed! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/dsc-cmt/dubbo-easy-mock.svg?branch=master)](https://travis-ci.org/dsc-cmt/dubbo-easy-mock) 2 | ## 这个框架的作用 3 | 在自动测试中,针对dubbo接口进行mock的框架 4 | 5 | ## 原理 6 | 利用dubbo的扩展点自动包装,通过EasyMockClusterWrapper将原本的rpc请求改写为http请求转发到mock服务器返回我们对应mock结果 7 | 8 | ![](img/principle.jpg) 9 | 10 | ## 使用 11 | ### 1. 添加依赖 12 | mvn clean package install (deploy) -Dmaven.test.skip=true 编译安装依赖到本地/远程仓库 13 | 14 | ```xml 15 | 16 | com.cmt 17 | dubbo-easy-mock 18 | 1.1.0 19 | 20 | 21 | org.apache.dubbo 22 | dubbo 23 | 24 | 25 | 26 | ``` 27 | ### 2. classpath增加mock.properties 28 | ``` 29 | ## 是否启用mock 30 | easymock.enable=true 31 | ## mock服务器地址 32 | easymock.server.url=https://easy-mock.com/mock/5c77afd53ecfbb573cba5df8/test 33 | ## 接口级别mock开关 34 | easymock.tests.HelloService=true 35 | ## 方法级别mock开关,优先级大于接口级别 36 | easymock.tests.HelloService.hello=true 37 | ``` 38 | 39 | ### 3. mock服务-请求约束 40 | 41 | 本框架对mock服务器不做限制,但是发送的mock请求需要遵循以下约束 42 | 1. 请求类型为GET,请求path为/接口全限定名/方法名 43 | 2. 返回格式为json 44 | 对于基本类型,因为json仅支持对象和数组两种结构,所以需要用以下格式嵌套一层 45 | ``` 46 | { 47 | "data":xxx 48 | } 49 | ``` 50 | 对于其他类型,比如对象,Map,Collection等其他类型,与FastJson生成的一致即可 51 | 52 | mock服务器推荐 [easy-mock](https://github.com/easy-mock/easy-mock)或者[mockserver](https://github.com/mock-server/mockserver)这两个项目 53 | 54 | ## 更新 55 | 2020-03-12 - 兼容apache和alibaba版本的dubbo 56 | 57 | ## 示例工程 58 | [dubbo-easy-mock-demo](https://github.com/dsc-cmt/dubbo-easy-mock-demo) 59 | 60 | ## 相关文章 61 | [针对Dubbo接口Mock的解决方案](https://www.jianshu.com/p/d71c7771b9c9) 62 | -------------------------------------------------------------------------------- /img/principle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsc-cmt/dubbo-easy-mock/a5e18febba25bfc1a054c6fade38d20ec4905d1a/img/principle.jpg -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.shengchaojie 8 | dubbo-easy-mock 9 | 1.1.0 10 | 11 | 12 | 1.8 13 | 2.6.8 14 | 4.3.2.RELEASE 15 | utf-8 16 | 1.8 17 | 1.8 18 | 19 | 20 | 21 | 22 | org.apache.dubbo 23 | dubbo 24 | 2.7.4.1 25 | 26 | 27 | 28 | org.projectlombok 29 | lombok 30 | 1.18.10 31 | provided 32 | 33 | 34 | 35 | org.apache.httpcomponents 36 | httpclient 37 | 4.5.10 38 | 39 | 40 | 41 | org.slf4j 42 | slf4j-api 43 | 1.7.29 44 | 45 | 46 | 47 | com.alibaba 48 | fastjson 49 | 1.2.62 50 | 51 | 52 | 53 | org.apache.curator 54 | curator-client 55 | 4.0.1 56 | test 57 | 58 | 59 | 60 | org.apache.curator 61 | curator-framework 62 | 2.7.0 63 | test 64 | 65 | 66 | 67 | org.apache.curator 68 | curator-recipes 69 | 2.7.0 70 | test 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | ch.qos.logback 82 | logback-classic 83 | 1.2.3 84 | test 85 | 86 | 87 | 88 | ch.qos.logback 89 | logback-core 90 | 1.2.3 91 | test 92 | 93 | 94 | 95 | org.spockframework 96 | spock-core 97 | 1.2-groovy-2.4 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.spockframework 105 | spock-bom 106 | 2.0-M1-groovy-2.5 107 | pom 108 | import 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.codehaus.gmavenplus 117 | gmavenplus-plugin 118 | 1.6 119 | 120 | 121 | 122 | compile 123 | compileTests 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dubbo/common/Node.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dubbo.common; 2 | 3 | /** 4 | * @author shengchaojie 5 | * @date 2020-03-12 6 | **/ 7 | public interface Node { 8 | 9 | /** 10 | * get url. 11 | * 12 | * @return url. 13 | */ 14 | URL getUrl(); 15 | 16 | /** 17 | * is available. 18 | * 19 | * @return available. 20 | */ 21 | boolean isAvailable(); 22 | 23 | /** 24 | * destroy. 25 | */ 26 | void destroy(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/dubbo/rpc/Invoker.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.dubbo.rpc; 2 | 3 | import com.alibaba.dubbo.common.Node; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2020-03-12 8 | **/ 9 | public interface Invoker extends Node { 10 | 11 | /** 12 | * get service interface. 13 | * 14 | * @return service interface. 15 | */ 16 | Class getInterface(); 17 | 18 | /** 19 | * invoke. 20 | * 21 | * @param invocation 22 | * @return result 23 | * @throws RpcException 24 | */ 25 | Result invoke(Invocation invocation) throws RpcException; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/EasyMockClusterWrapper.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des; 2 | 3 | import org.apache.dubbo.rpc.Invoker; 4 | import org.apache.dubbo.rpc.RpcException; 5 | import org.apache.dubbo.rpc.cluster.Cluster; 6 | import org.apache.dubbo.rpc.cluster.Directory; 7 | 8 | 9 | /** 10 | * 采用SPI的包装类 包装Cluster扩展点 11 | * 如果开启easymock 直接走easymock的逻辑 12 | * @author shengchaojie 13 | * @date 2019-06-17 14 | **/ 15 | public class EasyMockClusterWrapper implements Cluster { 16 | 17 | private Cluster cluster; 18 | 19 | public EasyMockClusterWrapper(Cluster cluster) { 20 | this.cluster = cluster; 21 | } 22 | 23 | @Override 24 | public Invoker join(Directory directory) throws RpcException { 25 | //防御性前置判断 26 | if(MockConfig.INSTANCE.isMockEnable()) { 27 | return new EasyMockInvoker<>(directory, this.cluster.join(directory)); 28 | } 29 | 30 | return this.cluster.join(directory); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/EasyMockHandler.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des; 2 | 3 | import com.cmt.des.util.ClassHelper; 4 | import com.cmt.des.util.HttpUtil; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.dubbo.rpc.AppResponse; 7 | import org.apache.dubbo.rpc.Invocation; 8 | import org.apache.dubbo.rpc.Invoker; 9 | import org.apache.dubbo.rpc.Result; 10 | 11 | import java.lang.reflect.Type; 12 | 13 | /** 14 | * @author shengchaojie 15 | * @date 2019-06-17 16 | **/ 17 | @Slf4j 18 | public class EasyMockHandler { 19 | 20 | 21 | public static Result invoke(Invoker invoker, Invocation invocation, String interfaceName, String methodName) { 22 | //mock总开关 23 | if (!MockConfig.INSTANCE.isMockEnable()) { 24 | return invoker.invoke(invocation); 25 | } 26 | 27 | if (MockConfig.INSTANCE.isMethodMockEnable(interfaceName, methodName)|| 28 | MockConfig.INSTANCE.isInterfaceMockEnable(interfaceName)) { 29 | String mockUrl = MockConfig.INSTANCE.buildMockRequestUrl(interfaceName, methodName); 30 | log.debug("mockUrl:{}", mockUrl); 31 | try { 32 | //通过easymock返回mock数据 33 | String mockValue = HttpUtil.doGet(mockUrl); 34 | //通过反射拿到返回的类型 35 | Type[] returnTypes = ClassHelper.getReturnType(interfaceName, methodName, invocation.getParameterTypes()); 36 | return new AppResponse(MockValueResolver.resolve(mockValue, returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null)); 37 | } catch (Exception e) { 38 | // TODO: 2020-02-08 同样的异常处理 39 | log.error("interface:{} method:{}mock失败,转为正常调用", interfaceName, methodName, e); 40 | } 41 | } 42 | //正常调用 43 | return invoker.invoke(invocation); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/EasyMockInvoker.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des; 2 | 3 | 4 | import org.apache.dubbo.common.URL; 5 | import org.apache.dubbo.rpc.Invocation; 6 | import org.apache.dubbo.rpc.Invoker; 7 | import org.apache.dubbo.rpc.Result; 8 | import org.apache.dubbo.rpc.RpcException; 9 | import org.apache.dubbo.rpc.cluster.Directory; 10 | 11 | /** 12 | * @author shengchaojie 13 | * @date 2019-06-17 14 | **/ 15 | public class EasyMockInvoker implements Invoker { 16 | 17 | private final Directory directory; 18 | 19 | private final Invoker invoker; 20 | 21 | public EasyMockInvoker(Directory directory, Invoker invoker) { 22 | this.directory = directory; 23 | this.invoker = invoker; 24 | } 25 | 26 | @Override 27 | public Class getInterface() { 28 | return directory.getInterface(); 29 | } 30 | 31 | @Override 32 | public Result invoke(Invocation invocation) throws RpcException { 33 | String interfaceName = invoker.getInterface().getCanonicalName(); 34 | String methodName = invocation.getMethodName(); 35 | return EasyMockHandler.invoke(invoker, invocation, interfaceName, methodName); 36 | } 37 | 38 | @Override 39 | public URL getUrl() { 40 | return directory.getUrl(); 41 | } 42 | 43 | @Override 44 | public boolean isAvailable() { 45 | //这边返回true 让check=true的情况也通过 46 | return true; 47 | } 48 | 49 | @Override 50 | public void destroy() { 51 | this.invoker.destroy(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/MockConfig.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des; 2 | 3 | import com.cmt.des.util.ConfigUtils; 4 | import lombok.Getter; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.Optional; 10 | import java.util.Properties; 11 | 12 | /** 13 | * @author shengchaojie 14 | * @date 2020-02-07 15 | **/ 16 | @Slf4j 17 | public class MockConfig { 18 | 19 | private static final String MOCK_FILE_NAME = "mock.properties"; 20 | 21 | public static final MockConfig INSTANCE = new MockConfig(); 22 | 23 | private static final String REQUEST_URL_FORMAT = "%s/%s/%s"; 24 | 25 | private Boolean enable; 26 | 27 | @Getter 28 | private String mockServerUrl; 29 | 30 | private Map interfaceMockConfig = new HashMap<>(); 31 | 32 | private MockConfig() { 33 | Properties mockProperties = ConfigUtils.loadProperties(MOCK_FILE_NAME); 34 | for(String key : mockProperties.stringPropertyNames()){ 35 | String value = mockProperties.getProperty(key); 36 | if("easymock.enable".equals(key)){ 37 | enable = "true".equals(value); 38 | }else if("easymock.server.url".equals(key)){ 39 | mockServerUrl = value; 40 | }else if(key.startsWith("easymock.")){ 41 | interfaceMockConfig.put(key.substring(9),"true".equals(value)); 42 | }else{ 43 | log.info("无效配置项:key={},value={}",key,value); 44 | } 45 | } 46 | } 47 | 48 | public Boolean isMockEnable(){ 49 | return Optional.ofNullable(enable).orElse(false); 50 | } 51 | 52 | public Boolean isInterfaceMockEnable(String interfaceName){ 53 | return Optional.ofNullable(interfaceMockConfig.get(interfaceName)).orElse(false); 54 | } 55 | 56 | public Boolean isMethodMockEnable(String interfaceName, String methodName){ 57 | return Optional.ofNullable(interfaceMockConfig.get(interfaceName+"."+methodName)).orElse(false); 58 | } 59 | 60 | public String buildMockRequestUrl(String interfaceName, String methodName){ 61 | return String.format(REQUEST_URL_FORMAT,mockServerUrl,interfaceName,methodName); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/MockValueResolver.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.cmt.des.util.ClassHelper; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * @author shengchaojie 10 | * @date 2020-02-09 11 | **/ 12 | public class MockValueResolver { 13 | 14 | public static Object resolve(String mockValue, Type type,Type genericType) { 15 | if (Void.TYPE.isAssignableFrom((Class) type)) { 16 | return null; 17 | } 18 | 19 | Object value = null; 20 | if (ClassHelper.isPrimitive((Class) type)) { 21 | //处理内置类型 22 | //解决easymock不支持基本类型返回的问题 23 | PrimitiveWrapper primitiveWrapper = JSON.parseObject(mockValue, PrimitiveWrapper.class); 24 | value = JSON.parseObject(primitiveWrapper.getData().toString(),type); 25 | } else if (mockValue.startsWith("{") || mockValue.startsWith("[")) { 26 | //处理普通对象 27 | value = JSON.parseObject(mockValue, genericType!=null?genericType:type); 28 | } else { 29 | // TODO: 2020-02-08 走到这边代表出错了 考虑错误如何处理 30 | value = mockValue; 31 | } 32 | 33 | return value; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/PrimitiveWrapper.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | 10 | /** 11 | * @author shengchaojie 12 | * @date 2019-03-04 13 | **/ 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class PrimitiveWrapper implements Serializable { 18 | 19 | private Object data; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/compatible/CompatibleEasyMockClusterWrapper.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.compatible; 2 | 3 | import com.alibaba.dubbo.rpc.Invoker; 4 | import com.alibaba.dubbo.rpc.RpcException; 5 | import com.alibaba.dubbo.rpc.cluster.Cluster; 6 | import com.alibaba.dubbo.rpc.cluster.Directory; 7 | import com.cmt.des.MockConfig; 8 | 9 | 10 | /** 11 | * 采用SPI的包装类 包装Cluster扩展点 12 | * 如果开启easymock 直接走easymock的逻辑 13 | * @author shengchaojie 14 | * @date 2019-06-17 15 | **/ 16 | public class CompatibleEasyMockClusterWrapper implements Cluster { 17 | 18 | private Cluster cluster; 19 | 20 | public CompatibleEasyMockClusterWrapper(Cluster cluster) { 21 | this.cluster = cluster; 22 | } 23 | 24 | @Override 25 | public Invoker join(Directory directory) throws RpcException { 26 | //防御性前置判断 27 | if(MockConfig.INSTANCE.isMockEnable()) { 28 | return new CompatibleEasyMockInvoker<>(directory, this.cluster.join(directory)); 29 | } 30 | 31 | return this.cluster.join(directory); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/compatible/CompatibleEasyMockHandler.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.compatible; 2 | 3 | import com.alibaba.dubbo.rpc.Invocation; 4 | import com.alibaba.dubbo.rpc.Invoker; 5 | import com.alibaba.dubbo.rpc.Result; 6 | import com.alibaba.fastjson.JSON; 7 | import com.cmt.des.MockConfig; 8 | import com.cmt.des.MockValueResolver; 9 | import com.cmt.des.util.ClassHelper; 10 | import com.cmt.des.util.HttpUtil; 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | import java.lang.reflect.Type; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | /** 18 | * @author shengchaojie 19 | * @date 2019-06-17 20 | **/ 21 | @Slf4j 22 | public class CompatibleEasyMockHandler { 23 | 24 | public static Result invoke(Invoker invoker, Invocation invocation, String interfaceName, String methodName) { 25 | //mock总开关 26 | if (!MockConfig.INSTANCE.isMockEnable()) { 27 | return invoker.invoke(invocation); 28 | } 29 | 30 | if (MockConfig.INSTANCE.isMethodMockEnable(interfaceName, methodName)|| 31 | MockConfig.INSTANCE.isInterfaceMockEnable(interfaceName)) { 32 | String mockUrl = MockConfig.INSTANCE.buildMockRequestUrl(interfaceName, methodName); 33 | log.debug("mockUrl:{}", mockUrl); 34 | try { 35 | //通过easymock返回mock数据 36 | String mockValue = HttpUtil.doGet(mockUrl); 37 | //通过反射拿到返回的类型 38 | Type[] returnTypes = ClassHelper.getReturnType(interfaceName, methodName, invocation.getParameterTypes()); 39 | final Object result = MockValueResolver.resolve(mockValue, returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null); 40 | log.info("mock 返回值:{}", JSON.toJSONString(result)); 41 | return new CompatibleResultAdaptor() { 42 | @Override 43 | public Object getValue() { 44 | return result; 45 | } 46 | 47 | @Override 48 | public Object recreate() throws Throwable { 49 | return result; 50 | } 51 | 52 | @Override 53 | public Map getAttachments() { 54 | return new HashMap<>(0); 55 | } 56 | 57 | @Override 58 | public boolean hasException() { 59 | return false; 60 | } 61 | }; 62 | } catch (Exception e) { 63 | // TODO: 2020-02-08 同样的异常处理 64 | log.error("interface:{} method:{}mock失败,转为正常调用", interfaceName, methodName, e); 65 | } 66 | } 67 | //正常调用 68 | return invoker.invoke(invocation); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/compatible/CompatibleEasyMockInvoker.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.compatible; 2 | 3 | 4 | import com.alibaba.dubbo.common.URL; 5 | import com.alibaba.dubbo.rpc.Invocation; 6 | import com.alibaba.dubbo.rpc.Invoker; 7 | import com.alibaba.dubbo.rpc.Result; 8 | import com.alibaba.dubbo.rpc.RpcException; 9 | import com.alibaba.dubbo.rpc.cluster.Directory; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | /** 13 | * @author shengchaojie 14 | * @date 2019-06-17 15 | **/ 16 | @Slf4j 17 | public class CompatibleEasyMockInvoker implements Invoker { 18 | 19 | private final Directory directory; 20 | 21 | private final Invoker invoker; 22 | 23 | public CompatibleEasyMockInvoker(Directory directory, Invoker invoker) { 24 | this.directory = directory; 25 | this.invoker = invoker; 26 | } 27 | 28 | @Override 29 | public Class getInterface() { 30 | return directory.getInterface(); 31 | } 32 | 33 | @Override 34 | public Result invoke(Invocation invocation) throws RpcException { 35 | String interfaceName = invoker.getInterface().getCanonicalName(); 36 | String methodName = invocation.getMethodName(); 37 | return CompatibleEasyMockHandler.invoke(invoker, invocation, interfaceName, methodName); 38 | } 39 | 40 | @Override 41 | public URL getUrl() { 42 | return directory.getUrl(); 43 | } 44 | 45 | @Override 46 | public boolean isAvailable() { 47 | //这边返回true 让check=true的情况也通过 48 | return true; 49 | } 50 | 51 | @Override 52 | public void destroy() { 53 | this.invoker.destroy(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/compatible/CompatibleResultAdaptor.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.compatible; 2 | 3 | import com.alibaba.dubbo.rpc.Result; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.*; 7 | import java.util.function.BiConsumer; 8 | import java.util.function.BiFunction; 9 | import java.util.function.Consumer; 10 | import java.util.function.Function; 11 | 12 | /** 13 | * 兼容 alibaba dubbo 和 apache dubbo的 Result 14 | * @author shengchaojie 15 | * @date 2020-03-10 16 | **/ 17 | public abstract class CompatibleResultAdaptor implements Result { 18 | @Override 19 | public Object getValue() { 20 | return null; 21 | } 22 | 23 | @Override 24 | public Throwable getException() { 25 | return null; 26 | } 27 | 28 | @Override 29 | public boolean hasException() { 30 | return false; 31 | } 32 | 33 | @Override 34 | public Object recreate() throws Throwable { 35 | return null; 36 | } 37 | 38 | @Override 39 | public Map getAttachments() { 40 | return null; 41 | } 42 | 43 | @Override 44 | public void addAttachments(Map map) { 45 | 46 | } 47 | 48 | @Override 49 | public void setAttachments(Map map) { 50 | 51 | } 52 | 53 | @Override 54 | public String getAttachment(String key) { 55 | return null; 56 | } 57 | 58 | @Override 59 | public String getAttachment(String key, String defaultValue) { 60 | return null; 61 | } 62 | 63 | @Override 64 | public void setAttachment(String key, String value) { 65 | 66 | } 67 | 68 | @Override 69 | public org.apache.dubbo.rpc.Result getNow(org.apache.dubbo.rpc.Result valueIfAbsent) { 70 | return null; 71 | } 72 | 73 | @Override 74 | public org.apache.dubbo.rpc.Result whenCompleteWithContext(BiConsumer fn) { 75 | return null; 76 | } 77 | 78 | @Override 79 | public CompletionStage thenApply(Function fn) { 80 | return null; 81 | } 82 | 83 | @Override 84 | public CompletionStage thenApplyAsync(Function fn) { 85 | return null; 86 | } 87 | 88 | @Override 89 | public CompletionStage thenApplyAsync(Function fn, Executor executor) { 90 | return null; 91 | } 92 | 93 | @Override 94 | public CompletionStage thenAccept(Consumer action) { 95 | return null; 96 | } 97 | 98 | @Override 99 | public CompletionStage thenAcceptAsync(Consumer action) { 100 | return null; 101 | } 102 | 103 | @Override 104 | public CompletionStage thenAcceptAsync(Consumer action, Executor executor) { 105 | return null; 106 | } 107 | 108 | @Override 109 | public CompletionStage thenRun(Runnable action) { 110 | return null; 111 | } 112 | 113 | @Override 114 | public CompletionStage thenRunAsync(Runnable action) { 115 | return null; 116 | } 117 | 118 | @Override 119 | public CompletionStage thenRunAsync(Runnable action, Executor executor) { 120 | return null; 121 | } 122 | 123 | @Override 124 | public CompletionStage thenCombine(CompletionStage other, BiFunction fn) { 125 | return null; 126 | } 127 | 128 | @Override 129 | public CompletionStage thenCombineAsync(CompletionStage other, BiFunction fn) { 130 | return null; 131 | } 132 | 133 | @Override 134 | public CompletionStage thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor) { 135 | return null; 136 | } 137 | 138 | @Override 139 | public CompletionStage thenAcceptBoth(CompletionStage other, BiConsumer action) { 140 | return null; 141 | } 142 | 143 | @Override 144 | public CompletionStage thenAcceptBothAsync(CompletionStage other, BiConsumer action) { 145 | return null; 146 | } 147 | 148 | @Override 149 | public CompletionStage thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor) { 150 | return null; 151 | } 152 | 153 | @Override 154 | public CompletionStage runAfterBoth(CompletionStage other, Runnable action) { 155 | return null; 156 | } 157 | 158 | @Override 159 | public CompletionStage runAfterBothAsync(CompletionStage other, Runnable action) { 160 | return null; 161 | } 162 | 163 | @Override 164 | public CompletionStage runAfterBothAsync(CompletionStage other, Runnable action, Executor executor) { 165 | return null; 166 | } 167 | 168 | @Override 169 | public CompletionStage applyToEither(CompletionStage other, Function fn) { 170 | return null; 171 | } 172 | 173 | @Override 174 | public CompletionStage applyToEitherAsync(CompletionStage other, Function fn) { 175 | return null; 176 | } 177 | 178 | @Override 179 | public CompletionStage applyToEitherAsync(CompletionStage other, Function fn, Executor executor) { 180 | return null; 181 | } 182 | 183 | @Override 184 | public CompletionStage acceptEither(CompletionStage other, Consumer action) { 185 | return null; 186 | } 187 | 188 | @Override 189 | public CompletionStage acceptEitherAsync(CompletionStage other, Consumer action) { 190 | return null; 191 | } 192 | 193 | @Override 194 | public CompletionStage acceptEitherAsync(CompletionStage other, Consumer action, Executor executor) { 195 | return null; 196 | } 197 | 198 | @Override 199 | public CompletionStage runAfterEither(CompletionStage other, Runnable action) { 200 | return null; 201 | } 202 | 203 | @Override 204 | public CompletionStage runAfterEitherAsync(CompletionStage other, Runnable action) { 205 | return null; 206 | } 207 | 208 | @Override 209 | public CompletionStage runAfterEitherAsync(CompletionStage other, Runnable action, Executor executor) { 210 | return null; 211 | } 212 | 213 | @Override 214 | public CompletionStage thenCompose(Function> fn) { 215 | return null; 216 | } 217 | 218 | @Override 219 | public CompletionStage thenComposeAsync(Function> fn) { 220 | return null; 221 | } 222 | 223 | @Override 224 | public CompletionStage thenComposeAsync(Function> fn, Executor executor) { 225 | return null; 226 | } 227 | 228 | @Override 229 | public CompletionStage exceptionally(Function fn) { 230 | return null; 231 | } 232 | 233 | @Override 234 | public CompletionStage whenComplete(BiConsumer action) { 235 | return null; 236 | } 237 | 238 | @Override 239 | public CompletionStage whenCompleteAsync(BiConsumer action) { 240 | return null; 241 | } 242 | 243 | @Override 244 | public CompletionStage whenCompleteAsync(BiConsumer action, Executor executor) { 245 | return null; 246 | } 247 | 248 | @Override 249 | public CompletionStage handle(BiFunction fn) { 250 | return null; 251 | } 252 | 253 | @Override 254 | public CompletionStage handleAsync(BiFunction fn) { 255 | return null; 256 | } 257 | 258 | @Override 259 | public CompletionStage handleAsync(BiFunction fn, Executor executor) { 260 | return null; 261 | } 262 | 263 | @Override 264 | public CompletableFuture toCompletableFuture() { 265 | return null; 266 | } 267 | 268 | @Override 269 | public boolean cancel(boolean mayInterruptIfRunning) { 270 | return false; 271 | } 272 | 273 | @Override 274 | public boolean isCancelled() { 275 | return false; 276 | } 277 | 278 | @Override 279 | public boolean isDone() { 280 | return false; 281 | } 282 | 283 | @Override 284 | public org.apache.dubbo.rpc.Result get() throws InterruptedException, ExecutionException { 285 | return null; 286 | } 287 | 288 | @Override 289 | public org.apache.dubbo.rpc.Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 290 | return null; 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/util/ClassHelper.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.util; 2 | 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Type; 9 | 10 | /** 11 | * @author shengchaojie 12 | * @date 2019-03-04 13 | **/ 14 | public class ClassHelper { 15 | 16 | private static final Logger logger = LoggerFactory.getLogger(ClassHelper.class); 17 | 18 | public static boolean isPrimitive(Class type) { 19 | return type.isPrimitive() 20 | || type == String.class 21 | || type == Character.class 22 | || type == Boolean.class 23 | || type == Byte.class 24 | || type == Short.class 25 | || type == Integer.class 26 | || type == Long.class 27 | || type == Float.class 28 | || type == Double.class 29 | || type == Object.class; 30 | } 31 | 32 | public static Type[] getReturnType(String interfaceName,String methodName,Class[] parameterTypes){ 33 | Class cls = ReflectUtils.forName(interfaceName); 34 | Method method = null; 35 | try { 36 | method = cls.getMethod(methodName, parameterTypes); 37 | } catch (NoSuchMethodException e) { 38 | logger.error("反射获取返回类型失败",e); 39 | return null; 40 | } 41 | return new Type[]{method.getReturnType(), method.getGenericReturnType()}; 42 | } 43 | 44 | public static ClassLoader getClassLoader(){ 45 | return getClassLoader(ClassHelper.class); 46 | } 47 | 48 | public static ClassLoader getClassLoader(Class clazz) { 49 | ClassLoader cl = null; 50 | try { 51 | cl = Thread.currentThread().getContextClassLoader(); 52 | } catch (Throwable ex) { 53 | // Cannot access thread context ClassLoader - falling back to system class loader... 54 | } 55 | if (cl == null) { 56 | // No thread context class loader -> use class loader of this class. 57 | cl = clazz.getClassLoader(); 58 | if (cl == null) { 59 | // getClassLoader() returning null indicates the bootstrap ClassLoader 60 | try { 61 | cl = ClassLoader.getSystemClassLoader(); 62 | } catch (Throwable ex) { 63 | // Cannot access system ClassLoader - oh well, maybe the caller can live with null... 64 | } 65 | } 66 | } 67 | 68 | return cl; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/util/ConfigUtils.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.InputStream; 9 | import java.net.URL; 10 | import java.util.*; 11 | import java.util.regex.Pattern; 12 | 13 | /** 14 | * @author shengchaojie 15 | * @date 2020-03-12 16 | **/ 17 | public class ConfigUtils { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(ConfigUtils.class); 20 | 21 | private static Pattern VARIABLE_PATTERN = Pattern.compile( 22 | "\\$\\s*\\{?\\s*([\\._0-9a-zA-Z]+)\\s*\\}?"); 23 | 24 | private ConfigUtils() { 25 | } 26 | 27 | 28 | public static Properties loadProperties(String fileName) { 29 | return loadProperties(fileName, false, false); 30 | } 31 | 32 | /** 33 | * Load properties file to {@link Properties} from class path. 34 | * 35 | * @param fileName properties file name. for example: dubbo.properties, METE-INF/conf/foo.properties 36 | * @param allowMultiFile if false, throw {@link IllegalStateException} when found multi file on the class path. 37 | * @param optional is optional. if false, log warn when properties config file not found!s 38 | * @return loaded {@link Properties} content.
    39 | *
  • return empty Properties if no file found. 40 | *
  • merge multi properties file if found multi file 41 | *
42 | * @throws IllegalStateException not allow multi-file, but multi-file exist on class path. 43 | */ 44 | public static Properties loadProperties(String fileName, boolean allowMultiFile, boolean optional) { 45 | Properties properties = new Properties(); 46 | // add scene judgement in windows environment Fix 2557 47 | if (checkFileNameExist(fileName)) { 48 | try { 49 | FileInputStream input = new FileInputStream(fileName); 50 | try { 51 | properties.load(input); 52 | } finally { 53 | input.close(); 54 | } 55 | } catch (Throwable e) { 56 | logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e); 57 | } 58 | return properties; 59 | } 60 | 61 | List list = new ArrayList(); 62 | try { 63 | Enumeration urls = ClassHelper.getClassLoader(ConfigUtils.class).getResources(fileName); 64 | list = new ArrayList(); 65 | while (urls.hasMoreElements()) { 66 | list.add(urls.nextElement()); 67 | } 68 | } catch (Throwable t) { 69 | logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t); 70 | } 71 | 72 | if (list.isEmpty()) { 73 | if (!optional) { 74 | logger.warn("No " + fileName + " found on the class path."); 75 | } 76 | return properties; 77 | } 78 | 79 | if (!allowMultiFile) { 80 | if (list.size() > 1) { 81 | String errMsg = String.format("only 1 %s file is expected, but %d dubbo.properties files found on class path: %s", 82 | fileName, list.size(), list.toString()); 83 | logger.warn(errMsg); 84 | } 85 | 86 | // fall back to use method getResourceAsStream 87 | try { 88 | properties.load(ClassHelper.getClassLoader(ConfigUtils.class).getResourceAsStream(fileName)); 89 | } catch (Throwable e) { 90 | logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e); 91 | } 92 | return properties; 93 | } 94 | 95 | logger.info("load " + fileName + " properties file from " + list); 96 | 97 | for (java.net.URL url : list) { 98 | try { 99 | Properties p = new Properties(); 100 | InputStream input = url.openStream(); 101 | if (input != null) { 102 | try { 103 | p.load(input); 104 | properties.putAll(p); 105 | } finally { 106 | try { 107 | input.close(); 108 | } catch (Throwable t) { 109 | } 110 | } 111 | } 112 | } catch (Throwable e) { 113 | logger.warn("Fail to load " + fileName + " file from " + url + "(ignore this file): " + e.getMessage(), e); 114 | } 115 | } 116 | 117 | return properties; 118 | } 119 | 120 | /** 121 | * check if the fileName can be found in filesystem 122 | * 123 | * @param fileName 124 | * @return 125 | */ 126 | private static boolean checkFileNameExist(String fileName) { 127 | File file = new File(fileName); 128 | return file.exists(); 129 | } 130 | 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/util/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.util; 2 | 3 | import org.apache.http.client.methods.CloseableHttpResponse; 4 | import org.apache.http.client.methods.HttpGet; 5 | import org.apache.http.impl.client.CloseableHttpClient; 6 | import org.apache.http.impl.client.HttpClients; 7 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 8 | import org.apache.http.util.EntityUtils; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * @author shengchaojie 14 | * @date 2019-03-04 15 | **/ 16 | public class HttpUtil { 17 | 18 | private static CloseableHttpClient closeableHttpClient; 19 | 20 | private final static Object syncLock = new Object(); 21 | 22 | private static CloseableHttpClient getHttpClient(){ 23 | if(closeableHttpClient==null){ 24 | synchronized (syncLock){ 25 | if(closeableHttpClient==null){ 26 | closeableHttpClient = createHttpClient(); 27 | } 28 | } 29 | } 30 | return closeableHttpClient; 31 | } 32 | 33 | private static CloseableHttpClient createHttpClient(){ 34 | //连接池默认设置不改了 35 | PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(); 36 | 37 | return HttpClients.custom() 38 | .setConnectionManager(poolingHttpClientConnectionManager) 39 | .build(); 40 | } 41 | 42 | public static String doGet(String url) throws IOException{ 43 | HttpGet httpGet = new HttpGet(url); 44 | CloseableHttpResponse httpResponse =getHttpClient().execute(httpGet); 45 | 46 | return EntityUtils.toString(httpResponse.getEntity(),"utf-8"); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/cmt/des/util/ReflectUtils.java: -------------------------------------------------------------------------------- 1 | package com.cmt.des.util; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.ConcurrentMap; 5 | 6 | /** 7 | * ReflectUtils 8 | */ 9 | public final class ReflectUtils { 10 | 11 | /** 12 | * void(V). 13 | */ 14 | public static final char JVM_VOID = 'V'; 15 | 16 | /** 17 | * boolean(Z). 18 | */ 19 | public static final char JVM_BOOLEAN = 'Z'; 20 | 21 | /** 22 | * byte(B). 23 | */ 24 | public static final char JVM_BYTE = 'B'; 25 | 26 | /** 27 | * char(C). 28 | */ 29 | public static final char JVM_CHAR = 'C'; 30 | 31 | /** 32 | * double(D). 33 | */ 34 | public static final char JVM_DOUBLE = 'D'; 35 | 36 | /** 37 | * float(F). 38 | */ 39 | public static final char JVM_FLOAT = 'F'; 40 | 41 | /** 42 | * int(I). 43 | */ 44 | public static final char JVM_INT = 'I'; 45 | 46 | /** 47 | * long(J). 48 | */ 49 | public static final char JVM_LONG = 'J'; 50 | 51 | /** 52 | * short(S). 53 | */ 54 | public static final char JVM_SHORT = 'S'; 55 | 56 | 57 | private static final ConcurrentMap> NAME_CLASS_CACHE = new ConcurrentHashMap>(); 58 | 59 | 60 | private ReflectUtils() { 61 | } 62 | 63 | 64 | public static Class forName(String name) { 65 | try { 66 | return name2class(name); 67 | } catch (ClassNotFoundException e) { 68 | throw new IllegalStateException("Not found class " + name + ", cause: " + e.getMessage(), e); 69 | } 70 | } 71 | 72 | 73 | /** 74 | * name to class. 75 | * "boolean" => boolean.class 76 | * "java.util.Map[][]" => java.util.Map[][].class 77 | * 78 | * @param name name. 79 | * @return Class instance. 80 | */ 81 | public static Class name2class(String name) throws ClassNotFoundException { 82 | return name2class(ClassHelper.getClassLoader(), name); 83 | } 84 | 85 | /** 86 | * name to class. 87 | * "boolean" => boolean.class 88 | * "java.util.Map[][]" => java.util.Map[][].class 89 | * 90 | * @param cl ClassLoader instance. 91 | * @param name name. 92 | * @return Class instance. 93 | */ 94 | private static Class name2class(ClassLoader cl, String name) throws ClassNotFoundException { 95 | int c = 0, index = name.indexOf('['); 96 | if (index > 0) { 97 | c = (name.length() - index) / 2; 98 | name = name.substring(0, index); 99 | } 100 | if (c > 0) { 101 | StringBuilder sb = new StringBuilder(); 102 | while (c-- > 0) { 103 | sb.append("["); 104 | } 105 | 106 | if ("void".equals(name)) { 107 | sb.append(JVM_VOID); 108 | } else if ("boolean".equals(name)) { 109 | sb.append(JVM_BOOLEAN); 110 | } else if ("byte".equals(name)) { 111 | sb.append(JVM_BYTE); 112 | } else if ("char".equals(name)) { 113 | sb.append(JVM_CHAR); 114 | } else if ("double".equals(name)) { 115 | sb.append(JVM_DOUBLE); 116 | } else if ("float".equals(name)) { 117 | sb.append(JVM_FLOAT); 118 | } else if ("int".equals(name)) { 119 | sb.append(JVM_INT); 120 | } else if ("long".equals(name)) { 121 | sb.append(JVM_LONG); 122 | } else if ("short".equals(name)) { 123 | sb.append(JVM_SHORT); 124 | } else { 125 | // "java.lang.Object" ==> "Ljava.lang.Object;" 126 | sb.append('L').append(name).append(';'); 127 | } 128 | name = sb.toString(); 129 | } else { 130 | if ("void".equals(name)) { 131 | return void.class; 132 | } 133 | if ("boolean".equals(name)) { 134 | return boolean.class; 135 | } 136 | if ("byte".equals(name)) { 137 | return byte.class; 138 | } 139 | if ("char".equals(name)) { 140 | return char.class; 141 | } 142 | if ("double".equals(name)) { 143 | return double.class; 144 | } 145 | if ("float".equals(name)) { 146 | return float.class; 147 | } 148 | if ("int".equals(name)) { 149 | return int.class; 150 | } 151 | if ("long".equals(name)) { 152 | return long.class; 153 | } 154 | if ("short".equals(name)) { 155 | return short.class; 156 | } 157 | } 158 | 159 | if (cl == null) { 160 | cl = ClassHelper.getClassLoader(); 161 | } 162 | Class clazz = NAME_CLASS_CACHE.get(name); 163 | if (clazz == null) { 164 | clazz = Class.forName(name, true, cl); 165 | NAME_CLASS_CACHE.put(name, clazz); 166 | } 167 | return clazz; 168 | } 169 | 170 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster: -------------------------------------------------------------------------------- 1 | easymock=com.cmt.des.compatible.CompatibleEasyMockClusterWrapper -------------------------------------------------------------------------------- /src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.Cluster: -------------------------------------------------------------------------------- 1 | easymock=com.cmt.des.EasyMockClusterWrapper -------------------------------------------------------------------------------- /src/test/java/tests/HelloService.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import tests.model.Person; 4 | import tests.model.Result; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * @author shengchaojie 10 | * @date 2019-06-17 11 | **/ 12 | public interface HelloService { 13 | 14 | String helloWorld(); 15 | 16 | Long testLong1(); 17 | 18 | long testLong2(); 19 | 20 | void empty(); 21 | 22 | Result genericMethod(); 23 | 24 | Result genericMethod2(); 25 | 26 | Person complexObjcet(); 27 | 28 | Map returnMapMethod(); 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/tests/application/EasyMockTest.groovy: -------------------------------------------------------------------------------- 1 | package tests.application 2 | 3 | 4 | import org.apache.dubbo.config.ApplicationConfig 5 | import org.apache.dubbo.config.ReferenceConfig 6 | import org.apache.dubbo.config.RegistryConfig 7 | import spock.lang.* 8 | import tests.HelloService 9 | 10 | /** 11 | * @author shengchaojie 12 | * @date 2019-06-17 13 | **/ 14 | class EasyMockTest extends Specification { 15 | 16 | private static HelloService helloService; 17 | 18 | def setupSpec(){ 19 | ApplicationConfig applicationConfig = new ApplicationConfig(); 20 | applicationConfig.setName("test-consumer"); 21 | 22 | RegistryConfig registryConfig = new RegistryConfig(); 23 | registryConfig.setAddress("zookeeper://127.0.0.1:2181"); 24 | 25 | ReferenceConfig referenceConfig = new ReferenceConfig(); 26 | referenceConfig.setInterface(HelloService.class); 27 | referenceConfig.setRegistry(registryConfig); 28 | referenceConfig.setApplication(applicationConfig); 29 | referenceConfig.setCheck(false); 30 | helloService = referenceConfig.get(); 31 | } 32 | 33 | def "test long"() { 34 | when : 35 | println helloService.testLong1() 36 | then: 37 | noExceptionThrown() 38 | when: 39 | helloService.testLong2() 40 | then: 41 | noExceptionThrown() 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/tests/component/MockValueResolverTest.groovy: -------------------------------------------------------------------------------- 1 | package tests.component 2 | 3 | import com.alibaba.fastjson.JSON 4 | import com.alibaba.fastjson.TypeReference 5 | import com.cmt.des.MockValueResolver 6 | import com.cmt.des.PrimitiveWrapper 7 | import spock.lang.Specification 8 | import tests.model.QueryCurNeedRepayResponse 9 | import tests.model.Result 10 | 11 | /** 12 | * @author shengchaojie* @date 2020-02-09 13 | * */ 14 | class MockValueResolverTest extends Specification { 15 | 16 | def "test parse primitive"() { 17 | when: 18 | def value = MockValueResolver.resolve(JSON.toJSONString(new PrimitiveWrapper(originValue)), type, genericType) 19 | then: 20 | Objects.equals(value, originValue) 21 | where: 22 | originValue | type | genericType 23 | 1 | Integer | null 24 | } 25 | 26 | def "test parse not primitive"() { 27 | when: 28 | def value = MockValueResolver.resolve(JSON.toJSONString(originValue), type, genericType) 29 | then: 30 | Objects.equals(value, originValue) 31 | where: 32 | originValue | type | genericType 33 | new Result(123) | Result | new TypeReference>() {}.type 34 | new Result>(new Result(123)) | Result | new TypeReference>>() { 35 | }.type 36 | } 37 | 38 | def "test Object"(){ 39 | given: 40 | String s = "{\n" + 41 | " \"code\": \"10000\",\n" + 42 | " \"interest\": 1,\n" + 43 | " \"msg\": \"成功\",\n" + 44 | " \"principal\": 0,\n" + 45 | " \"success\": true,\n" + 46 | " \"totalAmount\": 0\n" + 47 | "}"; 48 | when: 49 | def value = MockValueResolver.resolve(s, QueryCurNeedRepayResponse.class, null) 50 | then: 51 | Objects.equals(value, originValue) 52 | } 53 | 54 | 55 | } -------------------------------------------------------------------------------- /src/test/java/tests/model/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package tests.model; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2020-03-12 8 | **/ 9 | @Data 10 | public class BaseResponse { 11 | 12 | private Boolean success; 13 | private String code; 14 | private String msg; 15 | private String subCode; 16 | private String subMsg; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/tests/model/Person.java: -------------------------------------------------------------------------------- 1 | package tests.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author shengchaojie 9 | * @date 2020-02-07 10 | **/ 11 | @Data 12 | public class Person implements Serializable { 13 | 14 | private String s; 15 | 16 | private Long l; 17 | 18 | private long l1; 19 | 20 | private Integer i; 21 | 22 | private int i1; 23 | 24 | private Double d; 25 | 26 | private double d1; 27 | 28 | private Float f; 29 | 30 | private float f1; 31 | 32 | private Boolean b; 33 | 34 | private boolean b1; 35 | 36 | private Character c; 37 | 38 | private char c1; 39 | 40 | private Byte by; 41 | 42 | private byte by1; 43 | 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/tests/model/QueryCurNeedRepayResponse.java: -------------------------------------------------------------------------------- 1 | package tests.model; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author shengchaojie 7 | * @date 2020-03-12 8 | **/ 9 | @Data 10 | public class QueryCurNeedRepayResponse extends BaseResponse{ 11 | 12 | private Long totalAmount; 13 | private Long principal; 14 | private Long interest; 15 | private Long ovdAmount; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/tests/model/Result.java: -------------------------------------------------------------------------------- 1 | package tests.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author shengchaojie 8 | * @date 2020-02-07 9 | **/ 10 | @Data 11 | @AllArgsConstructor 12 | public class Result { 13 | 14 | private T data; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/resources/mock.properties: -------------------------------------------------------------------------------- 1 | ## 是否启用mock 2 | easymock.enable=true 3 | ## easymock baseurl 4 | easymock.server.url=https://easy-mock.com/mock/5c77afd53ecfbb573cba5df8/fsc 5 | easymock.tests.HelloService=true --------------------------------------------------------------------------------