码迷,mamicode.com
首页 > 其他好文 > 详细

浅谈WireMock结合Mock+Proxy应用于异常测试

时间:2015-06-14 12:23:18      阅读:1715      评论:0      收藏:0      [点我收藏+]

标签:

浅谈WireMock结合Mock+Proxy应用于异常测试


 

为什么需要WireMock

最近在做NCE自动化接口测试,按照尽可能覆盖逻辑的原则,写了200+用例,但实际去实现的时候,大概能做的就100不到,完成的大多是能通过传入参数去控制系统的逻辑走向和结果的case,即传入指定的参数,调用发送请求工具,对结果进行校验。那么除此之外,哪些是难以实现的呢?
从被测系统本身来说,NCE是建立在底层服务之上的一套系统,他的主要职能还是资源分配和调度,比如调云主机的接口拿两台机器,又去申请几块硬盘挂上,然后还要在机器上部署k8s的组件,调k8s创建rc和pod等,考虑到调用以异步为主,还要加上轮询,所以大量的调用外部的接口,以及结果判断的逻辑。而调用外部服务,想要获得预期的结果,并不是那么容易,需要额外了解什么情况下才会出现异常,有些情况难以模拟又不能跑去把公共的服务搞坏。这不仅仅是自动化容易遇到的问题,手工测试也同样会遇到。
这种上层服务测试所遇到的问题一般是怎么解决的呢。就之前的经验来说,需要借助mock来实现,比如之前在微信支付,后台依赖的是财付通的服务,很多异常需要由财付通后台返回,另外由于测试环境是对接的,财付通做异常测试或是环境出现问题的时候,微信支付的正常的测试活动就不能自理了,所以需要mock来降低对外部环境的依赖。仅有mock还是不够,如果一个人mock了一个服务,那么所有人都走到了被mock的服务上,如果被mock的返回是异常,那么所有人的测试活动又没有办法进行,如果被mock返回的是成功,对别人又是一种误导。所以最后采用的是一种mock+选择的模式,一般以帐号+接口名为维度来控制请求访问指定的目的地,如图
技术分享

如果要对NCE系统做异常测试,mock是比较容易找的,由于协议都是http协议而不是什么自封装的协议,所以选择比较多,其中WireMock是一种较好的选择,之前有同学已经推荐过了WireMock——轻量级HTTP Mock服务器),自己尝试过一下,也够简单易用。分发方面,可以考虑python的HTTPServer做一个转发服务器,收到请求后,解析请求串转发给想要的url,然后把请求返回回去
但还要加上规则匹配什么的就觉得心好累,后来发现WireMock自己就支持实时设定这种更为简单的方式,就转为全都依赖WireMock

首先要做的——外部访问的统一管理

目前的服务访问外部的url都是在配置文件中,如果每次都要改url配置指向mock,用mock测完再改回来无疑是一种低效的做法,结合WireMock的proxy功能希望能对所有可能的外部访问进行管理
使用WireMock proxy前的容器服务(举例)
技术分享

使用WireMock proxy后,我们所期望的
技术分享

按如下步骤让url都通过WireMock的proxy

1.下载并启动WireMock-xx-standalone.jar

下载的WireMock的standalone,放到某台主机上

基本的启动方式:

# java -jar wiremock-1.55-standalone.jar --verbose

java -jar wiremock-1.55-standalone.jar –help 有一些可以使用的命令,加–verbose打开verbose信息输出到屏幕方便调试 ,当服务器使用可以按如下命令挂在后台

# nuhup java -jar wiremock-1.55-standalone.jar &

 

欢迎页就是这个鸟样:
技术分享

然后将修改配置需要代理的链接切到WireMock,由上图看出WireMock默认端口8080,当然可以自己用–port指定。比如需要将kube的url从10.180.155.13:8080变为10.180.148.30:8080(WireMock端口)

启动过WireMock后目录下面有2个文件夹:mapping和__file。mapping里面是收发的规则配置,mapping里面指定了返回的文件名的话,就会从__file里面把文件给取出来,若规则匹配,则会返回对应的内容(预定的字符串或文件内容或网络错误),不匹配则返回404not found。
这里要使用proxy模式,请求通过wiremock进行代理,在mapping里面配置proxy规则,proxy的配置和mock配置类似,简单配置如下

新建container-mapping.json文件,加入一下配置
{
    "request": {
        "method": "GET",
        "urlPattern": "/api/v1beta2/.*"
    },
    "response": {
        "proxyBaseUrl" : "http://10.180.155.13:8080/"
    }
}

 

于是/api/v1beta2的http请求就从container->wiremock->kube,体验上却和直接container->kube一致


技术分享

WireMock日志

2015-05-30 12:09:48.36 Request received:
GET /api/v1beta2/pods?labels=podlabel%3Dapitest-d5e3e7cf9bee471f8ff642d1258e1354-go00o HTTP/1.1
User-Agent: curl/7.26.0
Host: 10.180.148.30:8080
Accept: */*


2015-05-30 12:09:48.76 Proxying: GET http://10.180.155.13:8080//api/v1beta2/pods?labels=podlabel%3Dapitest-d5e3e7cf9bee471f8ff642d1258e1354-go00o

 

其他http链接也可以用这种方式配置好

采用WireMock做自动化异常测试

现在已经让外部链接纳入WireMock的控制了,如果想做网络异常或指定返回,可以添加另一个规则,将这个url对应的请求都返回指定值,但必然只有一个生效,比如

{
    "request": {
        "method": "GET",
        "urlPattern": "/api/v1beta2/.*"
    },
    "response": {
        "proxyBaseUrl" : "http://10.180.155.13:8080/"
    }
}


{
    "request": {
        "method": "GET",
        "urlPattern": "/api/v1beta2/.*"
    },
    "response": {
        "status": 200,
        "body": "mocked by hzmali!\n"
        }
}

 

实际情况下,谁在前面就优先使用那个规则,是否要每次做完异常测试需要再改回配置到正常方式呢,如果是手工测试还可以接受,如果是自动化测试,那么就会很麻烦.幸运的是,WireMock 提供了通过__admin/mappings/new接口来远程配置规则的功能。

尝试在mapping下创建了一个test.json

{ "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "on disk!\n" }}

 

重启stansdalone,发现规则生效

curl "http://10.180.148.30:8080/get/this"
on disk!

 

调用__admin/mappings/new配置/get/this返回NO

# curl -X POST --data { "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "NO!\n" }} http://10.180.148.30:8080/__admin/mappings/new

# curl "http://10.180.148.30:8080/get/this"
NO!

 

再配置/get/this返回YES

# curl -X POST --data { "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "YES!\n" }} http://10.180.148.30:8080/__admin/mappings/new

# curl "http://10.180.148.30:8080/get/this"
YES!
 

重启standalone,之前配置的结果不存在,恢复为配置文件的结果:

#curl "http://10.180.148.30:8080/get/this"
on disk!

 

各位看官大概可以猜想到,stanalone启动时加载了本地的mapping配置到内存,/__admin/mappings/new这个接口将发送过去的规则置为最新规则,根据官方文档的说法,最新的规则会生效:
By default, WireMock will use the most recently added matching stub to satisfy the request. However, in some cases it is useful to exert more control.(exert more control 指的是使用”priority”: 1,这样的字段来指定优先级来破坏最近匹配的规则,目前没有怎么使用)

WireMock standalone能通过接口设置规则,以及最新生效这2个特性,对自动化用例绝对是一个喜大普奔的消息,在模拟外部资源返回特定消息或异常时,可以采取这样通用的方式:
1.正常case:外部访问都通过WireMock的proxy,映射关系都写在本地配置,WireMock启动就自动加载,不影响正常功能,上层无感知

2.需要mock的case:执行前将想要的返回以及匹配规则发送到/__admin/mappings/new接口使之生效,执行完成后重置规则使用proxy方式

简单的tesng case写法,通过设置规则控制返回是proxy的结果还是自己所期望的mock返回

/*WebUserParameterTestData.class的DataProvider*/
    @DataProvider(name = "sample")
    public static Object[][] sample(){
        String jsonString="{\"request\": {\"method\": \"GET\",\"urlPattern\": \"/api/v1beta2/pods.*\"},\"response\": {\"status\": 200,\"body\": \"mocked by hzmali\"}}";
        return new Object[][]{
                {jsonString, "and"},
        };
    }

/*case主要部分*/
    @BeforeClass
    @Parameters({ "env" })
    public void init(String env) throws IOException, InterruptedException {
        CommonData.init(env);
        nce_conn = new NceHttpBiz(CommonData.WEBHOST);
        mock_conn=new MockTool();
        //打印当前结果
        log.info("[befor test]response: "+nce_conn.getPods());
        //设置规则放到test里做

    }




    @AfterMethod
    public void recoverMapping() throws IOException{
        //恢复规则
        String rule="{\"request\": {\"method\": \"GET\",\"urlPattern\": \"/api/v1beta2/pods.*\"},\"response\": { \"proxyBaseUrl\" : \"http://10.180.155.13:8080/\"}}";;
        mock_conn.setMapping(rule);
        //打印恢复后的结果
        log.info("[after test]response: "+nce_conn.getPods());

    }



    @Test(dataProvider = "sample", dataProviderClass =WebUserParameterTestData.class)
    public void sample(String rule,String expeted) throws IOException
    {   

        System.out.println(rule);
        //设置期望的返回
        mock_conn.setMapping(rule);
        //打印预期的返回
        log.info("[testing]response: "+nce_conn.getPods());

    }

 

打印的结果表示符合预期

[INFO ]17:32:34, [Class]NceHttpBiz, [Method]getPods, ==========call getPods=============
[INFO ]17:32:34, [Class]MockCase, [Method]init, [befor test]response: {
  "kind": "Status",
  "creationTimestamp": null,
  "apiVersion": "v1beta2",
  "status": "Failure",
  "message": "invalid selector: ‘podlabel%3Dapitest-d5e3e7cf9bee471f8ff642d1258e1354-go00o‘; can‘t understand ‘podlabel%3Dapitest-d5e3e7cf9bee471f8ff642d1258e1354-go00o‘",
  "code": 500
}
[INFO ]17:32:34, [Class]MockTool, [Method]setMapping, ==========setMapping=============
{"request": {"method": "GET","urlPattern": "/api/v1beta2/pods.*"},"response": {"status": 200,"body": "mocked by hzmali"}}
[INFO ]17:32:34, [Class]NceHttpBiz, [Method]getPods, ==========call getPods=============
[INFO ]17:32:34, [Class]MockCase, [Method]sample, [testing]response: mocked by hzmali
[INFO ]17:32:34, [Class]MockTool, [Method]setMapping, ==========setMapping=============
[INFO ]17:32:34, [Class]NceHttpBiz, [Method]getPods, ==========call getThis=============
[INFO ]17:32:34, [Class]MockCase, [Method]recoverMapping, [after test]response: {
  "kind": "Status",
  "creationTimestamp": null,
  "apiVersion": "v1beta2",
  "status": "Failure",
  "message": "invalid selector: ‘podlabel%3Dapitest-d5e3e7cf9bee471f8ff642d1258e1354-go00o‘; can‘t understand ‘podlabel%3Dapitest-d5e3e7cf9bee471f8ff642d1258e1354-go00o‘",
  "code": 500
}

 

实际使用中,比如测试部署服务时,pod状态不正确的场景,需要把查询pod这个接口mock掉,kube其他接口都走proxy方式,实际用例会复杂一些

后记

WireMock的使用上也有一些不方便的地方:由于是proxy,所以如果有同名接口,需要使用额外的正则方式或是新的WireMock进程;目前期望的返回都是由用例来控制,可控性强,对于一些大粒度的异常场景模拟较好,如泛失败,网络错误等,但精确模拟各种场景下正确的返回则需要花一点功夫,构造符合数据相关性的返回也是一个挑战。目前只是初步使用,还有很多坑没踩到,乐观的来看这种方式能够解决一些问题,而且代价不高,可以拿来先顶顶。

 

浅谈WireMock结合Mock+Proxy应用于异常测试

标签:

原文地址:http://www.cnblogs.com/opama/p/4574821.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!