Moco接口框架应用实战

一、什么是Moco:
Moco 是一个搭建模拟服务器的工具,其支持 API 和独立运行两种方式,前者通常是在 junit 、testng等测试框架中使用,后者则是通过运行一个 jar 包开启服务。

二、Moco在开发中会起到的作用:
Moco是针对HTTP集成而生的,不过,现在也有人把它用在其它需要一个模拟服务器的场景中。比如,在移动开发中,有人开发一个移动应用,需要有一个远端服务,但在开发时,这个服务还不存在,他就用Moco模拟了一个服务,保证移动应用可以顺利的开发。同样,也有人把它用在Web前端开发里,当我们的页面需要通过与服务器交互时,就可以用Moco模拟这样一个服务。这种做法在开发一个页面原型时,非常有用,因为那个时候,我们还来不及开发一个完整的服务。

三、Moco在接口测试中的用处:
既然开发人员可以通过 Moco 模拟一个还不存在的服务来进行开发、调试,那对于接口测试来说,也可以模拟一个服务进行测试。
一般而言,在项目的接口文档输出后,开发人员会进行接口开发工作,测试人员会进行接口用例的设计,但往往完成用例设计会先于接口开发工作,此时如果要进行接口用例的执行,则前提是开发人员完成接口开发工作。
而通过 Moco 框架,就可以在接口文档输出后,在接口开发、接口用例设计的同时,使用 Moco 搭建一个模拟服务器,这样在用例设计完成后,即使接口开发工作还未完成,也可以立即进行执行接口用例,在这个过程中可以修改、补充用例,如此的话,在接口开发完成以后,只需要简单的去执行所有的用例就 OK,省去了很大的工作量,并且这些完善的用例,用自动化去执行,效果更佳。

四、Moco的应用:

本章会通过示例讲如下信息:
模拟get请求
模拟带参数的get请求
模拟post请求
模拟带form格式参数的post请求
模拟request请求中带cookies信息的get请求
模拟request请求中带cookies的post请求,post请求带json格式参数,返回结果也使用json格式
模拟response返回中带有cookies信息的get请求
模拟带有headers信息的mock请求
模拟有重定向的接口
使用httpclient调moco的接口

   Moco jar下载地址:

https://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/0.11.0/
下载红框中的内容:

image.png

把jar包放到项目根目录下:


image.png

新建一个json文件,文件名为startup1.json


image.png

json中内容为:

[
  {
    "description": "这是第一个moco操作,格式要写正确,中括号、大括号一定要有,description是描述,request是请求,uri是请求路径,response是返回结果,text是返回的是文本类型内容",
    "request": {
      "uri": "/demo"
    },
    "response": {
      "text": "this is the first moco demo"
    }
  }
]

格式一定要写对,中括号、大括号一定要有,description是描述,request是请求,uri是请求路径,response是返回结果,text是返回的是文本类型内容
进入项目目录中,执行命令启动服务:


image.png

java -jar ./moco-runner-0.11.0-standalone.jar http -p 8888 -c startup1.json
http:是指是http协议,-p 8888是端口号 -c是执行当前文件

然后在postman中调接口执行就可以了,默认是get请求:


image.png

如果在text中写中文,会出现乱码情况,需要加配置信息:

  {"description":"这是第二个moco操作,模拟返回值为中文",
    "request":{
      "uri":"/demotwo"
    },
    "response":{
      "text":"这是第二个demo"
    }

  }

在postman中调接口返回乱码:


image.png

在返回结果中加入:
"headers":{
"Content-Type":"text/html;charset=gbk"
}

{"description":"这是第二个moco操作,模拟返回值为中文",
    "request":{
      "uri":"/demotwo"
    },
    "response":{
      "text":"这是第二个demo",
      "headers":{
        "Content-Type":"text/html;charset=gbk"
      }

    }

  }

修改json文件不需要重启,服务热部署会自动重启


image.png

在浏览器中调接口能成功返回中文:


image.png

为什么要在response中加一个header呢?
是因为服务器要通过实体头(header)的形式告诉浏览器一些信息,Content-Type就是指服务器告诉浏览器它发送的数据属于什么文件类型。 "Content-Type":"text/html;charset=gbk"就是服务器告诉浏览器返回的内容是text/html格式要以html的格式展示,charset=gbk就是字符集,告诉浏览器可以解析中文字符信息。

扩展一个小知识:
gbk和utf8的区别
我们这里将以最简单最容易理解的方式来描述GBK和UTF8的区别,以及它们分别是什么。

GBK编码:是指中国的中文字符,其它它包含了简体中文与繁体中文字符,另外还有一种字符“gb2312”,这种字符仅能存储简体中文字符。

UTF-8编码:它是一种全国家通过的一种编码,如果你的网站涉及到多个国家的语言,那么建议你选择UTF-8编码。

GBK和UTF8有什么区别?

UTF8编码格式很强大,支持所有国家的语言,正是因为它的强大,才会导致它占用的空间大小要比GBK大,对于网站打开速度而言,也是有一定影响的。

GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。

本小节中使用的字符集是gbk而不是utf-8可能是网页过于简单的原因。

五、带参数的get方法实现

在上一个json中写也可以,新建一个json文件也可以,新建json文件后需要执行命令
java -jar ./moco-runner-0.11.0-standalone.jar -p 8888 -c startupget.json
重启服务
示例如下:

[
  {
    "description":"这是一个带参数的get请求",
    "request":{
      "uri":"/getdemo",
      "method":"get",
      "queries":{
        "name":"qinzhenxia",
        "age":"28"
      }
    },
    "response":{
      "text":"返回这是一个带参数的get请求",
      "headers":{
        "Content-Type":"text/html;charset=gbk"
      }
    }
  }
]

在浏览器中访问:


image.png

六、模拟不带参数的post请求:
"method":"post"

  {
    "description":"这是一个post请求",
    "request":{
      "uri":"/postdemo",
      "method":"post"
    },
    "response":{
      "text":"返回这是一个post请求",
      "headers":{
        "Content-Type":"text/html;charset=gbk"
      }
    }
  }

在jemeter工具中执行post接口:


image.png

image.png

七:模拟带form参数的post请求:
post请求参数格式是key:value,不能使用queries,要使用forms

  {
    "description":"这是一个带参数的post请求,请求参数格式是key:value,不能使用queries,要使用forms",
    "request":{
      "uri":"/postwithparam",
      "method":"post",
      "forms":{
        "name":"qinzhenxia",
        "age":"28"
      }},
      "response":{
        "text":"这是一个带表单参数的post请求",
        "headers":{
          "Content-Type":"text/html;charset=gbk"
        }
      }
    }

在jemeter中请求接口:

image.png

八、request请求中带cookies信息的get请求
增加cookies值的设置:

  {
    "description":"这是一个request请求中带cookies信息的get请求",
    "request":{
      "uri":"/getwithcookies",
      "method":"get",
      "cookies":{
        "login":"true"
      }
    },
    "response":{
      "text":"返回这是一个request请求中带cookies信息的get请求",
      "headers":{
        "Content-Type":"text/html;charset=gbk"
      }
    }
  }

在jemeter中请求接口时要先设置cookies信息:


image.png

cookies值设置:


image.png

执行结果:


image.png
image.png

九、request请求中带cookies的post请求
使用json格式的参数、返回结果也使用json格式

 {
   "description":"这是一个request请求中带cookies、使用json格式传参、返回结果为json格式的post请求",
   "request":{
     "uri":"/postwithcookiesandjson",
     "method":"post",
     "cookies":{
       "login":"true"
     },
     "json":{
       "name":"qinzhenxia",
       "age":"28"
     }
   },
   "response":{
     "status":200,
     "json":{
       "code":"0",
       "msg":"success",
       "p2pdata":{
         "name":"moguzhixing",
         "address":"beisanhuan"
       }

     }
   }
 }

使用jemeter调接口,注意要先把cookie加入cookie管理器中:


image.png

请求接口的参数要写到bodydata中 是json格式的入参:


image.png

执行接口查看返回结果:


image.png

response请求中返回中文信息:

{
    "description":"这是一个request请求中带cookies、使用json格式传参、返回结果为json格式的post请求,json返回中有中文,json可以解析中文",
    "request":{
      "uri":"/postwithcookiesandjsontwo",
      "method":"post",
      "cookies":{
        "login":"true"
      },
      "json":{
        "name":"moguzhixing",
        "age":"28"
      }
    },
    "response":{
      "status":200,
      "json":{
        "code":"0",
        "msg":"success",
        "p2pdata":{
          "name":"和平西桥",
          "address":"北三环中路"
        }

      }
    }
  }

在jemeter中把cookies加入cookie管理器中,执行接口


image.png

十、模拟response返回中带有cookies信息的get请求

{
    "description":"模拟response返回中带有cookies信息的get请求",
    "request":{
      "uri":"/getcookies"
    },
    "response":{
      "cookies":{
        "login":"true",
        "token":"1234567890"

      },
      "json":{
        "name":"zhoujielun",
        "age":"38"
      }
    }
  }

请求返回结果:


image.png

十一、带有headers信息的mock请求
十二、moco有重定向的接口
需要用到"redirectTo":"/path",代码如下:

  {
    "description":"模拟重定向接口",
    "request":{
      "uri":"/redirectto"
    },
    "redirectTo":"/redirectedpath"
  },
  {
    "description":"模拟被重定向的接口",
    "request":{
      "uri":"/redirectedpath"
    },
    "response":{
      "text":"返回重定向结果",
      "headers":{
        "Content-Type":"text/html;charset=gbk"
      }
    }
  }

在浏览器中输入http://localhost:8889/redirectto查看结果:

image.png

十三、使用httpclient调moco接口
httpclient是模拟客户端发送http请求并获取返回结果的编程工具包,httpclient的使用方法:
使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。

  1. 创建HttpClient对象。

  2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。

  3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。

  4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。

  5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。

  6. 释放连接。无论执行方法是否成功,都必须释放连接

首先要加入依赖包,因为要用到testng,也加入testng的依赖包

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.9.10</version>
        </dependency>

加入依赖包后,写入不带参数的get请求

package com.course.httpclient.demo;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.testng.annotations.Test;

import java.io.IOException;

public class MyHttpClient {

    //加入@Test注解
    @Test
    public void testHttpClient() throws IOException {

        //定义httpclient对象
        HttpClient httpClient1=new DefaultHttpClient();
        //HttpGet类,参数为要执行的url,实际测试中可以为接口地址
        HttpGet httpGet=new HttpGet("http://localhost:8889/redirectto");

        //用httpclient对象执行httpGet,并把返回结果存到httpresponse中
        HttpResponse httpResponse=httpClient1.execute(httpGet);
        //httpResponse的结果要调getEntity方法接收,同时需要转换成String类型才可以打印出来。
        String result= EntityUtils.toString(httpResponse.getEntity());
        System.out.println(result);
        ((DefaultHttpClient) httpClient1).close();
  


    }
}

运行后查看结果:


image.png

十四、模拟从上一个接口中获取cookie,供下一个接口入参使用
moco的返回cookie的get接口代码如下:

  {
    "description":"模拟返回接口中有一个cookies,该cookie被用到下一个接口中的情况",
    "request":{
      "uri":"/response/cookies",
      "method":"get"
    },
    "response":{
      "cookies":{
        "token":"1234567890"
      },
      "json":{
        "name":"kunling",
        "age":25
      }
    }
  }

moco的携带cookie的post接口如下:
cookies的内容必须跟上一个接口的cookies内容一致才行。

{
"description":"这是一个request请求中带cookies、使用json格式传参、返回结果为json格式的post请求",
"request":{
  "uri":"/post/with/cookies/usejson",
  "method":"post",
  "cookies":{
    "token":"1234567890"
  },
  "headers":{
    "Content-Type":"application/json;charset=gbk"
  },
  "json":{
    "name":"qinzhenxia",
    "age":"28"
  }
},
"response":{
  "status":200,
  "json":{
    "code":"0",
    "msg":"success",
    "p2pdata":{
      "name":"moguzhixing",
      "address":"北三环"
    }
  }
}
}

httpclient调接口代码如下:

package com.course.httpclient.cookies;

import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

public class MyCookiesForPost {
    //定义url存储请求地址
    private String url;
    //定义resourcebundle 从配置文件中获取配置新
    private ResourceBundle resourceBundle;
    //设置cookiestore全局变量
    private CookieStore cookieStore;

    @BeforeTest
    public void BeforeTest(){
        //调用getBundle方法,告诉resourceBundle从哪个配置文件中获取配置信息,定义Locale.CHINA是中文信息
        resourceBundle=ResourceBundle.getBundle("application", Locale.CHINA);
        //传入test.url配置名称,获取配置信息并赋值给url
        url=resourceBundle.getString("test.url");
    }

    /*
模拟httpclient请求,并返回结果
 */
    @Test(enabled = false)
    public void testGetCookies() throws IOException {
        //拼接url
        String uri=url+resourceBundle.getString("test.responsecookies");

        //httpclient请求接口获取返回结果并打印
        HttpClient httpClient=new DefaultHttpClient();
        HttpGet httpGet=new HttpGet(uri);
        HttpResponse response=httpClient.execute(httpGet);
        String result= EntityUtils.toString(response.getEntity());
        System.out.println(result);
        ((DefaultHttpClient) httpClient).close();


    }
        /*
    模拟获取接口中返回的cookie,本接口返回的cookie供下一个接口使用。
     */

    @Test
    public void testGetAndResponseCookie() throws IOException {
        //拼接url
        String uri=url+resourceBundle.getString("test.responsecookies");


        //使用DefaultHttpClient类生成对象,不再使用HttpClient,这样才能获取到cookie
        DefaultHttpClient defaultHttpClient=new DefaultHttpClient();

        //使用getCookieStore()方法获取cookies信息,并赋值给cookieStore 全局变量
        cookieStore=defaultHttpClient.getCookieStore();

        //httpget请求
        HttpGet httpGet=new HttpGet(uri);
        HttpResponse httpResponse=defaultHttpClient.execute(httpGet);
        String result=EntityUtils.toString(httpResponse.getEntity());
        System.out.println(result);

        //获取cookies信息并打印出来
        List<Cookie> cookieStoreList=cookieStore.getCookies();

        //使用foreach循环返回cookie的name和value信息
        for(Cookie cookie : cookieStoreList){
            String name=cookie.getName();
            String value=cookie.getValue();
            System.out.println("cookiename:"+name+",cookievalue:"+value);
        }
        defaultHttpClient.close();


    }

    /*
    从上一个接口中获取cookie信息作为本接口的cookie
     */
    //使用dependsOnMethods = {"testGetAndResponseCookie"}参数,依赖testGetAndResponseCookie方法,也就是上一个方法返回的cookie信息
    @Test(dependsOnMethods = {"testGetAndResponseCookie"})
    public void testpostmethod() throws IOException {
        String uri=url+resourceBundle.getString("test.postwithcookiesusejson");

        //生成httpclient对象
        DefaultHttpClient defaultHttpClient=new DefaultHttpClient();

        //创建post请求
        HttpPost httpPost=new HttpPost(uri);

        //使用json入参,入参是中文时不行。
        JSONObject param=new JSONObject();
        param.put("name","qinzhenxia");
        param.put("age","28");

        //设置请求头header
        httpPost.setHeader("Content-Type","application/json;charset=gbk");
        //传入参数
        StringEntity entity=new StringEntity(param.toString(),"gbk");
        httpPost.setEntity(entity);

        //设置cookie是用defaultHttpClient调setCookieStore方法,不是用httppost调
        defaultHttpClient.setCookieStore(this.cookieStore);

        //执行请求并接收返回结果
        HttpResponse httpResponse = defaultHttpClient.execute(httpPost);
        String result=EntityUtils.toString(httpResponse.getEntity());
        System.out.println(result);


        //判断返回code值
        int statuscode=httpResponse.getStatusLine().getStatusCode();
        Assert.assertEquals(statuscode,200);

        //获取返回结果并转换为jsonobject格式,然后获取某个具体的值通过断言判断
        JSONObject resultjson=new JSONObject(result);
        String msg =resultjson.getString("msg");
        //判断msg等于success
        Assert.assertEquals(msg,"success");


        JSONObject p2pdata=resultjson.getJSONObject("p2pdata");

        String address=p2pdata.getString("address");
        System.out.println(address);
        Assert.assertEquals(address,"北三环");

    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,601评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,748评论 6 342
  • (看到第七章,不知道是自己情绪的原因还是什么,总觉得看不进去,强迫自己看看,又觉得很多细节实在重复。) ...
    筱筱吖頭阅读 143评论 0 0
  • 因为没有学过画画,只靠自己摸索,大家可以给点点评吗
  • 玄觉在佛门中修悟多年,一直都不能知道自己的证悟怎样了。尽管他也不在乎这些,但有机会见到高僧,他还是很高兴的。他很佩...
    道_元阅读 764评论 0 51