转载:http://blog.csdn.net/ronghuanye/article/details/71124127

1        简介

Dubbo目前的应用已经越来越广泛、或者基于Dubbo二次开发的也越来越多,使用到Dubbo的系统基本也是采用微服务架构设计的系统,多个系统、多个应用之间的接口是有依赖关系的,所以就会出现需要MOCK的应用场景。

当我们发布了两个应用A和B,应用A引用应用B发布的接口,那么我们的应用场景是MOCK应用B(接口提供方)。通常的MOCK方式是,测试人员自己发布一个一样的接口(应用C),然后把应用A的调用指向(应用C),这种方式可以解决MOCK,但是这种方式的弊端:

1.需要开发MOCK代码

2.对人员要求技能高

3.需要接口提供方的依赖jar

4.需要容器发布应用C

5.接口变更需要更新MOCK代码

6.发布时间长,需要编码发布


我们可以采取另外一个方式,通过添加dubbo的filter过滤器,通过过滤器拦截请求,把请求导向mock平台,或者拦截请求后直接返回已经缓存的响应数据,达到mock的效果而且可以配置返回的数据内容和响应时间。filter是放在消费端的(应用A),配置filter之后应用A的请求首先会到filter里面。

下面是添加filter后需要实现的工作流程图:

具体的filter代码实现请看下面的章节介绍!

RPC接口mock平台架构图:

2        RPC接口mock平台在接口测试中的用处

2.1             屏蔽关联接口依赖关系,快速定位接口的性能瓶颈点

当被测试接口依赖多个其他接口时,出现了性能问题后无法定位性能瓶颈是出在被测接口还是其他依赖的接口,这时可以使用接口mock平台屏蔽掉其他依赖的接口,这样就可以很快的定位是被测接口存在性能问题,还是其他依赖接口存在性能问题。

2.2             用mock平台代替依赖服务的部署,提高环境部署的效率,减少环境部署的工作量。

当进行集群测试时,需要部署大量的其他依赖服务器,如果依赖的服务部署比较繁琐,工作量较大时,可以考虑使用mock平台代替依赖服务的部署,既节省服务器资源又可提高部署环境的效率。

2.3             使用mock平台,可以方便查看被测试接口的依赖关系

从mock平台配置界面或者数据库可以看到被测接口依赖的其他接口列表:

2.4             使用mock平台的配置界面可以方便配置接口的mock参数(返回数据,mock开关,响应时间)

可以在mock平台的前端配置界面配置接口的mock参数,例如返回数据,mock开关,响应时间等。

3        RPC接口mock测试平台使用方法

3.1             实现com.alibaba.dubbo.rpc.Filter接口

实现com.alibaba.dubbo.rpc.Filter接口的类CustomConsumerFilter 代码如下:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import net.sf.json.JSONObject;
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcResult;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

@SuppressWarnings("deprecation")
public class CustomConsumerFilter implements Filter {
public static Map<String, Object> map = new HashMap<String, Object>(); //保存接口对象
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    Result result = null; 
 //定义一个返回对象;
    String interfaceName = invoker.getUrl().getPath();    //获取接口服务名称
String methodName = invocation.getMethodName();  //获取接口方法名称
Object[] arguments =invocation.getArguments();        //获取参数集合
List<String> paramTypes = new ArrayList<String>();
 //获取参数类型
for(int i=0; i<arguments.length; i++)
{
paramTypes.add(arguments[i].getClass().getTypeName());
}

Method method = null;

try {
method = Class.forName(interfaceName)
.getDeclaredMethod(methodName, invocation.getParameterTypes());
//获取调用方法
} catch (Exception e) {
e.printStackTrace();
}
Type returnType = method.getGenericReturnType();
 //获取返回类型
JSONObject responeJSON = new JSONObject();
if(map.containsKey(interfaceName + "+" + methodName + "+" + paramTypes.toString()))
{
responeJSON = (JSONObject) map.get(interfaceName + "+" + methodName + "+" + paramTypes.toString());

if(Integer.parseInt(responeJSON.getString("status"))==1)
{
String returnStr = responeJSON.getString("returnJson");
      Object returnJson = null;
//定义响应对象
      try {
      returnJson = JSON.parseObject(returnStr,Class.forName(returnType.getTypeName()));
} catch (Exception e) {
e.printStackTrace();
}
      
      result = new RpcResult();
//定义返回结果
      ((RpcResult) result).setResult(returnJson);
      ((RpcResult) result).setException(null);
}
else {
result = invoker.invoke(invocation);   //调用真实接口
}
}
else {
String url = "http://mock.vmall.com/tcep/";
//mock服务器地址
JSONObject jsonObj = new JSONObject();
String paramJson =JSON.toJSONString(arguments, SerializerFeature.WriteClassName);
//序列化入参为JSON串
      //请求测试平台查询接口mock信息
      jsonObj.put("interfaceName", interfaceName);
      jsonObj.put("methodName", methodName);
      jsonObj.put("paramTypes", paramTypes.toString());
      jsonObj.put("paramJson", paramJson);
      jsonObj.put("returnType", returnType.getTypeName());
      
      String resp = httpPost(url + "mock!goMock.action", jsonObj.toString());
responeJSON = JSONObject.fromObject(resp);
if(responeJSON.getBoolean("success"))
{
String mess = responeJSON.getString("message");
if(!mess.equals(""))
{
JSONObject responeJSON2 = JSONObject.fromObject(mess);
map.put(interfaceName + "+" + methodName + "+" + paramTypes.toString(), responeJSON2);
if(Integer.parseInt(responeJSON2.getString("status"))==1)
{
String returnStr = responeJSON2.getString("returnJson");
      Object returnJson = null;
//定义响应对象
      try {
      returnJson = JSON.parseObject(returnStr,Class.forName(returnType.getTypeName()));
} catch (Exception e) {
e.printStackTrace();
}
      
      result = new RpcResult();
//定义返回结果
      ((RpcResult) result).setResult(returnJson);
      ((RpcResult) result).setException(null);
}
else {
result = invoker.invoke(invocation);   //调用真实接口
}
} else {
result = invoker.invoke(invocation);   //调用真实接口
//然后把接口信息存到测试平台
jsonObj.put("returnJson", JSON.toJSONString(result.getResult()));
httpPost(url + "mock!addMock.action", jsonObj.toString());
}
}
}
        return result;
}
    /*  
  * 发送Http post请求
*/
    @SuppressWarnings("finally")
public String httpPost(String url,String jsonParam){
    DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
HttpResponse response = null;
String respStr = "";
try {
StringEntity entity = new StringEntity(jsonParam,"utf-8");
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
post.setEntity(entity);
response = client.execute(post);
//获取响应数据
InputStreamReader reader = new InputStreamReader(response.getEntity().getContent(),"UTF-8");
BufferedReader br = new BufferedReader(reader);
String line = null;
StringBuilder sb = new StringBuilder();
while((line = br.readLine())!=null){
sb.append(line);
}
respStr = sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
finally {
client.close();
return respStr;
}
    }
}

Filter类的java工程如下:

注意:需要在工程中创建目录META-INF/dubbo,然后在目录中创建文件com.alibaba.dubbo.rpc.Filter,文件内容添加

CustomConsumerFilter=com.huawei.vmall.mock.CustomConsumerFilter

将上面的java类打成jar包

导出的jar包如下所示:

3.2             Cosumer端添加filter过滤器,加入依赖包,配置hosts映射地址

1、添加filter过滤器

在被测系统中找到dubbo配置文件,tomcat部署的应用一般是

webapps/ROOT/WEB-INF/classes/spring目录中的dubbo-context.xml

Jar包启动的应用一般是lib/conf/spring目录中的dubbo-context.xml

找到consumer配置,在filter中添加CustomConsumerFilter参数:

2、加入依赖包

filter类需要依赖的jar包如下:

在被测系统的lib目录中添加filter类依赖的jar和上一步导出的filter类jar包,filter类依赖的前面4个jar包一般都会已经存在lib目录中了,不需要再重新添加,后面3个jar如果已经存在则不需要添加,不存在则添加。

Tomcat方式启动的应用lib目录

Jar包方式启动的应用lib目录

注意:添加jar前先检查是否已经存在,版本不同也没有关系,如果已经存在则不需添加。

3、配置hosts映射地址

在被测系统服务器中添加hosts配置

其中10.41.150.52是mock服务器的ip地址,mock.vmall.com是mock服务器的域名,域名已经写死在filter类中不能改,ip地址可以根据实际部署ip进行修改。

3.3             调试被测接口,添加接口mock

添加完成filter和jar包后需要重新启动被测系统,然后在eclipse中调试RPC接口,调试成功后会在mock数据库中添加依赖的接口信息。

Mock数据库插入的数据,其中status为0表示没有开启mock,需要手工在界面或者数据库中改1才能开启mock;要保证接口返回数据的正确性,可以在界面或者数据库中修改returnJson字段修改返回数据。

3.4             验证接口mock是否正常

当开启了接口mock之后,在jmeter中执行接口测试场景,验证接口mock是否正常,需要查看jmeter是否报错,被测系统的日志是否报错,查看接口调用日志中调用依赖接口的响应时间是否为0或者为预定的值。

4      常见问题

1、调用RPC接口时日志报下面的错误

解决办法:

由于缺少以下相关依赖包

dubbo-2.5.3.jar,fastjson-1.1.15.jar,httpclient-4.5.2.jar,httpcore-4.4.4.jar,json-lib-2.4-jdk15.jar,ezmorph-1.0.6.jar,commons-collections-3.2.1.jar,将依赖包加入被测试系统的lib目录下就可以了(先检查一下lib目录下面是否已经存在相关jar包,版本不同也没有关系,如果已经存在则不需添加)

RPC接口mock测试的更多相关文章

  1. rpc接口mock平台

    转载:http://blog.csdn.net/ronghuanye/article/details/71124320 1.简介 平台采用struts.spring.mybatis框架开发设计,主要用 ...

  2. 接口Mock测试

    什么是Mock测试? Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取的比较复杂的对象(如 JDB ...

  3. 《如何进行接口mock测试》

    前言: Mock通常是指:在测试一个对象时,我们构造一些假的对象来模拟与其交互.而这些Mock对象的行为是我们事先设定且符合预期.通过这些Mock对象来测试对象在正常逻辑,异常逻辑或压力情况下工作是否 ...

  4. 利用eolinker实现api接口mock测试(mock server)

    转载:http://blog.csdn.net/naicha_qin/article/details/78276172 前后端分离或者是进行单元测试的时候,必须要用mock api替换掉第三方调用或者 ...

  5. Mock测试接口

    Mock使用场景: (1)创建所需的DB数据可能需要很长时间,如:调用别的接口,模拟很多数据,确保发布版本接口可用 (2)调用第三方API接口,测试很慢, (3)编写满足所有外部依赖的测试可能很复杂, ...

  6. Mock测试你的Spring MVC接口

    1. 前言 在Java开发中接触的开发者大多数不太注重对接口的测试,结果在联调对接中出现各种问题.也有的使用Postman等工具进行测试,虽然在使用上没有什么问题,如果接口增加了权限测试起来就比较恶心 ...

  7. mock测试框架Mockito

    无论是敏捷开发.持续交付,还是测试驱动开发(TDD)都把单元测试作为实现的基石.随着这些先进的编程开发模式日益深入人心,单元测试如今显得越来越重要了.在敏捷开发.持续交付中要求单元测试一定要快(不能访 ...

  8. Spring MVC如何测试Controller(使用springmvc mock测试)

    在springmvc中一般的测试用例都是测试service层,今天我来演示下如何使用springmvc mock直接测试controller层代码. 1.什么是mock测试? mock测试就是在测试过 ...

  9. 使用mockserver来进行http接口mock

    转载自:https://blog.csdn.net/heymysweetheart/article/details/52227379:(注,这个不是很符合我的要求,它主要的作用是可以通过简单的代码就能 ...

随机推荐

  1. codeforces ~ 1009 B Minimum Ternary String(超级恶心的思维题

    http://codeforces.com/problemset/problem/1009/B B. Minimum Ternary String time limit per test 1 seco ...

  2. Location of ESXi 5.1 log files

    Purpose This article provides the default location of log files on an ESXi 5.1 host. For other produ ...

  3. Hibernate的注释该如何使用?每一个注释代表什么意思?

    出自:java快快飞 原文地址:http://blog.sina.com.cn/s/blog_697b968901016s31.html Hibernate的注释该如何使用?每一个注释代表什么意思? ...

  4. php 字符串重要函数

    1.chop() 从字符串右端移除字符 chop(string,charlist) $str="hello world~"; echo chop($str,"ld~&qu ...

  5. RSA加密/解密 Decryption error异常解决

    RSA加密/解密 Decryption error异常解决 import java.io.ByteArrayOutputStream; import java.security.Key; import ...

  6. 自以为是而已,不知道它是什么 window.onload 放执行

    var $=jQuery=function(onload){window.onload=onload();} jQuery(function(){alert(2);}); $(function(){a ...

  7. vs2015 建立项目报错:值不能为空,参数名:path1的错误解决与“未将对象引用到对象的实例”

    “值不能为空,参数名:path1” 的错误.原因就是安卓sdk的路径不正确. 最简单的解决办法如下: 找到C:\Program Files (x86)\Android\android-sdk.进入文件 ...

  8. JS基础用法-向数组指定位置插入对象

    在做省市区三级联动的时候,需要在省市区默认位置放上请选择字样. 由于后台的API接口返回的没有请选择字样,那么就需要给返回的数组手动增加请选择 代码如下 // 原来的数组 var array = [& ...

  9. HttpWebRequest 请求 Api 及 异常处理

    HttpWebRequest request = WebRequest.CreateHttp(url); request.Method = "post"; request.Head ...

  10. kong流程学习

    kong: 根据Nginx的不同执行阶段,kong先执行初始化master和worker进程. init_by_lua_block { require 'resty.core' kong = requ ...