系统测试利器之挡板实战(五)

2020-03-10  本文已影响0人  老杜杂谈

脚本注入

前面我们说了谓词的注入,其实就是大同小异。
那么我们看下它的参数属性有哪些

领域 描述
request 请求数据都在这
state mountebank全局变量,每次重启mb都会清空
callback 一般情况下函数有返回值,如果没有那就是通过异步处理,此时必须使用callback调用该参数。
logger log句柄的引用,在函数中可以直接使用输出日志

那么我们先看下代码

{
    "port": 8084,
    "protocol": "http",
    "stubs": [{
        "responses": [{
            "inject": "<%- stringify(filename, './test/responses.ejs') %>"
            }]
    }]
}

注入模板脚本responses.ejs如下:

function(config) {
    config.logger.info('origin called');
    config.state.requests = config.state.requests || 0;
    config.state.requests += 1;
        var ret={
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ count: config.state.requests })
      };
            return ret;
}

其实从代码我们可以看出,上面的参数都是通过config这个对象传递给模板脚本的

state可以存放json数据、基本数据类型等。

如果修改为callback那么代码如下:

function(config) {
    config.logger.info('origin called');
    config.state.requests = config.state.requests || 0;
    config.state.requests += 1;
    var ret={
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ count: config.state.requests })
    };
    config.callback(ret);
}

动态行为

_behaviors这个参数通过上面的讲解,应该大家了解一二,他的任务就是修改响应的数据,那么他的行为如下:

行为 描述
wait 响应延迟毫秒数
repeat 在转到下一个响应之前,重复一定次数的响应。
copy 将请求字段中的一个或多个值复制到响应中。您可以使用正则表达式,xpath或jsonpath标记响应并从请求字段中选择值。
lookup 从请求的数据中作为key值从外部数据源去取数据。
decorate --allowInjection启动参数必须加在发送之前使用JavaScript注入对响应进行后处理。
shellTransform --allowInjection启动参数必须加,不是使用JavaScript注入,而是将其发送到另一个应用程序,另一应用通过stdout输出JSON格式传递数据回来。

那么我们接下来对上面的参数详细说明下:

wait参数
{
  "port": 8085,
  "protocol": "http",
  "stubs": [
    {
      "responses": [
        {
          "is": {
            "body": "This took at least half a second to send"
          },
          "_behaviors": {
            "wait": 500
          }
        }
      ]
    }
  ]
}

通过http://localhost:8085访问后:

This took at least half a second to send
repeat参数
{
  "port": 8085,
  "protocol": "http",
  "stubs": [
    {
      "responses": [
        {
          "is": {
            "body": "This will repeat 2 times"
          },
          "_behaviors": {
            "repeat": 2
          }
        },
        {
          "is": {
            "body": "Then this will return"
          }
        }
      ]
    }
  ]
}

通过http://localhost:8085访问后:

//第一次结果
This will repeat 2 times
//第二次结果
This will repeat 2 times
//第三次结果
Then this will return

从上线的结果可以看出,如果没有"repeat": 2话应该是轮询显示结果。加了以后就可以访问多少次后再往下轮询。

copy参数
{
  "port": 8085,
  "protocol": "http",
  "stubs": [
    {
      "responses": [
        {
          "is": {
            "body": "The request name was ${name}. Hello, ${name}!"
          },
          "_behaviors": {
            "copy": [
              {
                "from": { "query": "name" },
                "into": "${name}",
                "using": {
                  "method": "regex",<!-- 用到正则表达式 -->
                  "selector": "MOUNT\\w+$",
                  "options": { "ignoreCase": true }<!-- 不区分大小写 -->
                }
              }
            ]
          }
        }
      ]
    }
  ]
}

上面的代码其实就是利用请求的参数中,查找包含以mount开头的数据,然后把它替换掉name变量,
通过http://localhost:8085/400?ignore=this&name=1mountebank23访问以后,因为包含1mountebank23值,所以显示结果如下:

The request name was mountebank23. Hello, mountebank23!

如果访问http://localhost:8085/400?ignore=this&name=1mou1ntebank23,因为没有匹配
那么结果如下:

The request name was ${name}. Hello, ${name}!
lookup参数

这个参数就是从请求的数据中,取出某个值作为查询条件去外部数据源查找对应的值。
代码如下:

{
  "port": 8085,
  "protocol": "http",
  "stubs": [
    {
      "responses": [
        {
          "is": {
            "body": "Hello ${row}['Name'], have you done your ${row}['jobs'] today?"
          },
          "_behaviors": {
            "lookup": [{<!-- 查找开始 -->
              "key": {
                "from": "path",<!-- 从请求路径中查找 -->
                "using": { "method": "regex", "selector": "/(.*)$" },<!-- 根据斜杠匹配数据 -->
                "index": 1<!-- 0会是原路径,1是去除/的值-->
              },
              "fromDataSource": {
                "csv": {
                  "path": "./test/values.csv",
                  "keyColumn": "Name"
                }
              },
              "into": "${row}"
            }]
          }
        }
      ]
    }
  ]
}

那么外部数据源的文件内容如下:

values.csv
State_ID,code,Name,price,tree,jobs
1111111,400,liquid,235651,mango,farmer
9856543,404,solid,54564564,orange,miner
2222222,500,water,12564,pine,shepherd
1234564,200,plasma,2656,guava,lumberjack
9999999,200,lovers,9999,dogwood,steel worker

那我通过http://localhost:8085/water访问时结果如下:

Hello water, have you done your shepherd today?

简单说明下为什么上面是index是1,懂正则的人一看就明白,但对初学者还是一个解释好,

如果路径是/water 通过下面的表达式
 "using": { "method": "regex", "selector": "/(.*)$" }
 得到的其实是个数组{"/water",water}

 index为1就取得值为water

得到water后,然后再去values.csv文件获取到对应的行数据。

decorate参数

咱们前面在说代理是说到一个属性addDecorateBehavior,它就可以生成对应的decorate,它的的目的就是动态修改或追加响应的数据。
就是利用javascript脚本注入的方式来完成。

代码上来再说哈!

{
  "port": 8085,
  "protocol": "http",
  "stubs": [
    {
      "responses": [
        {
          "is": {
            "body": "The time is ${TIME}"
          },
          "_behaviors": {
            "decorate": "(config) => { var pad = function (number) { return (number < 10) ? '0' + number : number.toString(); }, now = new Date(), time = pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds()); config.response.body = config.response.body.replace('${TIME}', time); }"
          }
        }
      ]
    }
  ]
}

通过http://localhost:8085访问后,结果如下:

The time is 23:06:29

当然也可以引入模板:

"_behaviors": {
              "decorate": "<%- stringify(filename, './test/behaviors.ejs') %>"
          }

behaviors.ejs模板文件的内容:

function(config) {
    var pad = function(number) {
        return (number < 10) ? '0' + number: number.toString();
    },
    now = new Date(),
    time = pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds());
    config.response.body = config.response.body.replace('${TIME}', time);
}
shellTransform参数

shellTransform行为类似于decorate,可以对响应数据进行修改。但不同的地方是它通过shell命令调用外部的脚本,
执行时他会把request、response作为参数传给外部脚本,外部脚本拿到后可以对响应的数据进行修改,然后输出到控制台,这样mb就可以达到对应的值,千万注意控制台打印的是json格式。

具体代码如下:

{
    "port": 8086,
    "protocol": "http",
    "stubs": [{
        "responses": [{
            "is": {
                "body": "Hello, ${city}!"
            },
            "_behaviors": {
                "shellTransform": ["node ./test/shellTransform.js"]
            }
        }]
    }]
}

shellTransform.js文件如下

// 使用 Mock
var Mock = require('mockjs')
var request = JSON.parse(process.argv[2]),
    response = JSON.parse(process.argv[3]);
response.body = response.body.replace('${city}', Mock.mock('@city'));
console.log(JSON.stringify(response));

那么通过http://localhost:8086访问后的结果如下:

Hello, 厦门市!

那么我们可以联想下,能够shellTransform.js使用外部数据源,解耦性更强了,只要外部输出json数据到控制台就可以拿到数据了。
shellTransform.js本身可以是nodejs,那么连接数据库、nosql都是分钟级的事情了,有包含上下文的业务场景挡板不就直接搞定了。

至此mountebank的使用已经讲解完了,有不明白的可以和我联系(微信号dcs678),加之前请备注mountebank或mb,否则拒绝通过。
接着看后面的实战干活哈,go on!

系统测试利器之挡板实战(一)
系统测试利器之挡板实战(二)
系统测试利器之挡板实战(三)
系统测试利器之挡板实战(四)
系统测试利器之挡板实战(六)
系统测试利器之挡板实战终结(七)

上一篇 下一篇

猜你喜欢

热点阅读