MockLab:基于MockLab的第三方平台对接测试

一、背景

现因系统需要对接各种第三方平台,但是因为:

  1. 需要检查发送给第三方的请求是否正确
  2. 在第三方未完成功能时,需要模拟桩来模拟第三方各种响应,代码做相应的处理
  3. 在测试环境时,无法对接到正式的代码,故需要模拟桩来模拟第三方

故使用MockLab来模拟第三方平台,辅助测试人员测试。

MockLab不仅仅可以应用于这些场景,还可以:

  1. 后台开发人员模拟第三方平台的响应来自测代码是否处理正确
  2. 前端开发人员可以模拟后台处理代码的响应来辅助前端页面开发

二、前言

MockLab是基于WireMock,类同于WireMock的在线版,地址为MockLab。

MockLab需要注册账户并登录后才可以使用,且免费版对api的个数有要求,具体参考MockLab的说明文档。

详细内容可以参考说明文档。

三、简易流程

1.新增Mock API

MockLab:基于MockLab的第三方平台对接测试_第1张图片

 

输入APIname、hostname,保存即可。

MockLab:基于MockLab的第三方平台对接测试_第2张图片

 

2.新增API

输入name、url和response,保存即可。

MockLab:基于MockLab的第三方平台对接测试_第3张图片

 

3.发送请求测试

MockLab:基于MockLab的第三方平台对接测试_第4张图片

 

四、操作指导

4.1请求匹配

4.1.1匹配URLs

  • path :匹配URL的路径,不匹配查询部分
  • path + query:匹配URL的路径和查询部分

例如:匹配规则是

/my/path?q=abc&limit=10

        请求URL是

A:https://my-api.mocklab.io/my/path?q=abcB:https://my-api.mocklab.io/my/path?q=abc&limit=10C:https://my-api.mocklab.io/my/pathD:https://my-api.mocklab.io/my/path?randomqueryparam=123

如果匹配策略是path时,ABCD都可以匹配。

如果匹配测试是path + query时,B才可以匹配,ACD匹配失败。

  • Path regex使用正则表达式匹配URL的路径,不匹配查询部分
  • path + queryregex使用正则表达式匹配URL的路径和查询部分

例如:匹配规则是

/users/[0-9]+?q=[a-zA-Z]{1,4}&limit=10

        请求URL是

A:/users/1B:/users/9832749823?q=abc&limit=10C:/users/321?q=abcde&limit=10D: /users/9832749823?p=abc&limit=10

如果匹配策略是pathregex时,ABCD都可以匹配。

如果匹配测试是path + query regex时,B才可以匹配,ACD匹配失败。

  • Any URL:匹配任何请求URL,该情况下可以使用其他方式来匹配

4.1.2匹配JSON请求

equalToJson

仅当输入JSON中的所有元素都与期望的JSON相同,数组的顺序相同并且不存在其他元素时,才会匹配。字典中的元素顺序不会影响匹配。

可选择:

  • 忽略list顺序的差异:勾选后list的元素的顺序不影响匹配。
  • 忽略额外的对象属性:勾选后字典中多了额外的元素也不会影响匹配。

如果要检查是否存在某个元素,但是不在乎值是什么,则可以使用JSONUnit占位符语法来实现。使用${json-unit.ignore}时,元素的类型也将被忽略(除了其值之外)。

如果要将元素限制为特定类型,但仍然忽略该值,则可以使用以下占位符之一:

${json-unit.regex}[A-Z]+ (可以使用任何Java风格的正则表达式)${json-unit.any-string}${json-unit.any-boolean}${json-unit.any-number}

matchesJsonPath

允按照指定的Xpath路径去匹配元素。

可选择:

  • equalTo:仅当输入字符串恰好等于期望值时才匹配
  • Contains:如果输入字符串包含期望值,则匹配
  • Matches regex:将输入字符串与Java样式正则表达式匹配
  • Ispresent:当该元素存在,则匹配

常见的匹配示例:

1.按位置匹配特定数组元素

$.sizes[1] equal to M

将匹配:

{

  "sizes":["S", "M", "L"]

}

匹配通过另一个元素找到的对象的一个​​元素。

$.addresses[?(@.type == 'business')].postcode contains N11NN

将匹配:

{

  "addresses":[

{

      "type":"home",

      "postcode":"Z55ZZ"

},

{

      "type":"business",

      "postcode":"N11NN"

}

]

}

contains在此实例中必须使用包含查询部分(在[?和之间])的JSONPath表达式,该表达式将始终返回结果集合。

递归匹配匹配的元素。

$..postcode contains N11NN

将匹配:

{

  "addresses":[

{

      "type":"home",

      "postcode":"Z55ZZ"

},

{

      "type":"business",

      "postcode":"N11NN"

}

]

}

并且还将匹配:

{

"address":{

"type":"business",

"postcode":"N11NN"

}

}

4.1.3匹配XML正文

Xml和json的匹配方式有些类似。

equalToXml

匹配操作者进行对照预期XML输入XML的语义比较,它会忽略空白中的差异、忽略元素和属性顺序。如果所有元素和属性都存在,具有相同的值并且没有其他元素或属性,则默认情况下会将输入与期望的XML匹配。

如果要检查是否存在某个元素,但是不在乎值是什么,则可以使用XMLUnit占位符语法来实现。使用${xmlunit.ignore实现。

matchesXPath

允按照指定的Xpath路径去匹配元素。

常见的匹配示例:

按位置匹配元素

/things/thing[@name = 'socks']

将匹配:

{

"sizes":["S","M","L"]

}

匹配通过另一个元素找到的对象的一个​​元素。

$.addresses[?(@.type == 'business')].postcode contains N11NN

将匹配:


name="socks">
name="shoes">

4.1.4匹配类型

MockLab(通过WireMock)支持一组匹配操作,可用于请求的查询,标头,Cookie和正文。且同一种匹配支持多个。

  • equalTo:仅当输入字符串恰好等于期望值时才匹配。
  • binaryEqualTo:喜欢equalTo但比较字节而不是字符串。当您需要匹配上传图像时很有用。
  • Matches:将输入字符串与Java样式正则表达式匹配。
  • doesNotMatch:matches相反集。如果传入值与正则表达式不匹配,则将匹配。
  • Contains:如果输入字符串包含期望值,则匹配。
  • equalToJson:如果输入字符串是有效的JSON并且在语义上等于期望值,则匹配。equalTo与处理JSON相比,这通常是一个更好的选择,因为它将忽略空格中的差异,并且可以选择忽略数组顺序和其他对象属性。它还提供了占位符的概念,使您可以有选择地忽略或仅约束特定的JSON元素。
  • matchesJsonPath:针对JSONPath表达式测试输入的JSON字符串,如果返回一个或多个元素,则返回匹配项。如果输入的JSON无效,则不会返回任何匹配项。
  • equalToXml:如果输入字符串是有效的XML,并且在语义上等于期望值,则匹配。像matchesJsonPath这样,忽略空格的差异并支持占位符,以便可以忽略特定的元素值。
  • matchesXPath:针对XPath 1.0表达式测试输入的XML字符串,如果返回一个或多个元素,则返回匹配项。如果输入的XML无效,则不会返回任何匹配项。

4.2响应

4.2.1基础

响应可以通过使用模板对响应的内容的进行模拟化

通过在“响应”部分中选中“启用模板”框来启用存根模板:

MockLab:基于MockLab的第三方平台对接测试_第5张图片

 

勾选此框意味着可以对header和body进行模板化。

4.2.2条件逻辑和迭代

条件逻辑

常见的if / else if / else逻辑

{{#ifshowVariantA}}
id="var-a">...
{{elseifshowVariantB}}
id="var-b">...
{{elseifshowVariantC}}
id="var-c">...
{{else}}
id="default-var">...
{{/if}}

如果条件为假,则可以执行以下操作unless

{{#unlesshideDetails}}
id="details">...
{{/unless}}

比较函数

如果您需要检查变量是否等于特定字符串,则可以使用eq

{{#eqname'Dan'}}
id="dan">...
{{elseeqname'Mark'}}
id="mark">...
{{else}}
id="anon">...
{{/eq}} 比较函数:
  • eq -相等
{{#eqname'Jeff'}}...{{/eq}}
  • neq -不相等
{{#neqname'Jeff'}}...{{/neq}}
  • gt - 比...更棒
{{#gtitemCount3}}...{{/gt}}
  • gte -大于或等于
{{#gteitemCount3}}...{{/gte}}
  • lt - 少于
{{#ltitemCount3}}...{{/lt}}
  • lte -小于或等于
{{#lteitemCount3}}...{{/lte}}
  • and -逻辑与
{{#and(ltitemCount10)(gtitemCount5)}}...{{/and}}
  • or -逻辑或
{{#or(eqitemCount1)(eqitemCount2)}}...{{/or}}
  • not -逻辑非
{{#not(eqitemCount1)}}...{{/not}}

4.2.3迭代

使用each来循环收集数据,例如

{{#eachrequest.query.thingsas|thing|}}  thing: {{{thing}}}{{/each}}

例如:

  • 循环时检测第一个和最后一个元素,例如
{{#each(jsonPathrequest.body'$.things')as|thing|}}
{{#if@last}}    { "thing": {{{thing}}} }{{else}}    { "thing": {{{thing}}} },{{/if}}
{{/each}}
  • 获取循环索引,例如
{{#each(jsonPathrequest.body'$.things')as|thing|}}
{{@index}}: {{thing}}
{{/each}}

4.2.4字符串

正则表达式

正则表达式提取可以提取单个值,例如

{{regexExtractrequest.body'[A-Z]+'}}

也可以将多个部分提取到对象中以供以后使用(最后一个参数是将为其分配对象的变量名称),例如

{regexExtractrequest.body'([a-z]+)-([A-Z]+)-([0-9]+)''parts'}{{parts.0}},{{parts.1}},{{parts.2}}

字符串转换

  • Trm:从输入的开头和结尾删除空格,例如
{{trimrequest.headers.X-Padded-Header}} // Inline
{{#trim}}                                // Block    Some stuff with whitespace{{/trim}}
  • Abbreviate:如果字符串长于指定的字符数,则将其截断。截断的字符串将以可翻译的省略号序列(“…”)结尾,例如
{{abbreviate'Mocking APIs helps you develop faster'21}} // Mocking APIs helps...
  • capitalize :将传递的字符串中每个单词的首字母大写,例如

{{capitalize'mock my stuff'}} // Mock My Stuff

  • capitalizeFirst:将大写通过的值的第一个字符,例如
{{capitalizeFirst'mock my stuff'}} // Mock my stuff
  • center :将值居中在给定宽度的字段中,例如
{{center'hello'size=21}}

将输出:

        hello        

您还可以指定填充字符,例如

{{center'hello'size=21pad='#'}}

将输出:

########hello########
  • cut 从给定的字符串中删除参数的所有实例
{{cut'mocking, stubbing, faults'','}} // mocking stubbing faults
  • defaultIfEmpty 如果不为空,则输出传递的值,否则为默认值,例如
{{defaultIfEmpty'my value''default'}} // my value
{{defaultIfEmpty'''default'}}         // default
  • join 接受一组参数或一个集合,并构建一个字符串,每一项均由指定参数分隔。
{{join'Mark''Rob''Dan'', '}} // Mark, Rob, Dan

您可以选择指定前缀和后缀:

{{join'Mark''Rob''Dan'', 'prefix='['suffix=']'}} // [Mark, Rob, Dan]
  • ljust 将值在给定宽度的字段中左对齐,可以选择填充字符。
{{ljust'things'size=20}}         // 'things              '
{{ljust'things'size=20pad='#'}} // 'things##############'

rjust 以相同的方式将值右对齐

{{rjust'things'size=20}}         // '             things'
{{rjust'things'size=20pad='#'}} // '##############things'
  • lower和upper值转换为全部小写和大写所有:
{{lower'MockLab'}} // mocklab
{{upper'MockLab'}} // MOCKLAB
  • replace 用替换值替换所有出现的指定子字符串。
{{replace'the wrong way''wrong''right'}} // the right way
  • slugify转换为小写字母,删除非单词字符(字母数字和下划线),并将空格转换为连字符。还去除前导和尾随空格。
{{slugify'Mock my APIs'}} // mock-my-apis
  • stripTags 剥离所有[X] HTML标记。
{{stripTags'hi'}} // hi
  • substring输出两个索引之间的字符串值部分。如果仅指定一个索引,则将返回此点和结尾之间的子字符串。
{{substring'one two'4}}   // two
{{substring'one two'03}} // one
  • wordWrap 以指定的行长包装单词。
{{wordWrap'one two three'4}}

将输出:

onetwothree
  • yesno truefalse和可选的null值映射到字符串“ yes”“ no”“ maybe”
{{yesno true}}   // yes
{{yesno false}}  // no
{{yesno null}}   // maybe

您还可以指定不同的字符串来表示每个状态:

{{yesno trueyes='aye'}}    // aye
{{yesno falseno='nay'}}    // nay
{{yesnonullmaybe='meh'}}  // meh

字符串编码

  • 辅助编码和解码的Base64
{{{base64request.headers.X-Plain-Header}}}
{{{base64request.headers.X-Plain-Headerpadding=false}}}
{{{base64request.headers.X-Encoded-Headerdecode=true}}}
{{{#base64}}}Content to encode {{{/base64}}}
{{{#base64decode=true}}}Q29udGVudCB0byBkZWNvZGUK{{{/base64}}}
  • urlEncode
{{{urlEncoderequest.headers.X-Plain-Header}}}
{{{urlEncoderequest.headers.X-Encoded-Headerdecode=true}}}
{{{#urlEncode}}}Content to encode    {{{/urlEncode}}}
{{{#urlEncodedecode=true}}}Content%20to%20decode    {{{/urlEncode}}}
  • formData

将请求主体解析为表单,然后输出单个字段formField3:

{{formDatarequest.body'form'urlDecode=true}}{{{form.formField3}}

如果提交的表单具有给定字段的多个值,则可以通过索引访问这些值:

{{formDatarequest.body'form'urlDecode=true}}}{{{form.multiValueField.1}}, {{{form.multiValueField.2}}
{{formDatarequest.body'form'urlDecode=true}}}{{{form.multiValueField.first}}, 
{{{form.multiValueField.last}}

4.2.5日期和时间 

当前日期/时间

Now:呈现当前日期/时间,以指定格式的能力

{{now}}
{{nowoffset='3 days'}}
{{nowoffset='-24 seconds'}}
{{nowoffset='1 years'}}
{{nowoffset='10 years'format='yyyy-MM-dd'}}

日期可以在特定时区(默认为UTC)呈现:

{{nowtimezone='Australia/Sydney'format='yyyy-MM-dd HH:mm:ssZ'}}

通过epoch作为格式来呈现日期作为UNIX信号出现时间(毫秒),或unix如以使以秒为UNIX时间戳的格式。

{{nowoffset='2 years'format='epoch'}}
{{nowoffset='2 years'format='unix'}}

已存在的日期值:更改偏移量,时区和打印格式

{{datemyDateoffset='-1 days'timezone='EST'format='yyyy-MM-dd'}}

从字符串解析日期

{{date(parseDaterequest.headers.MyDate)offset='-1 days'}}

格式化日期

使用dateFormat助手将日期值格式化为字符串。您可以从以下选项中选择一种命名格式:

  • full:完整日期格式。例如:2012年6月19日,星期二
  • long:长日期格式。例如:2012年6月19日
  • medium:中日期格式。例如:2012年6月19日
  • short:短日期格式。例如:6/19/12

例如

{{dateFormat(parseDate'2020-01-01T11:11:11Z')'full'}} // Wednesday, January 1, 2020

或者,您可以指定自己的格式字符串(在此完整参考):

{{dateFormat(parseDate'2020-01-01T11:11:11Z')format='yyyy-MM-dd'}} // 2020-01-01

格式化和解析日期和时间时使用的所有格式字符串元素:

MockLab:基于MockLab的第三方平台对接测试_第6张图片

 

4.2.6随机值

randomValue

randomValue辅助生成特定类型和长度的随机串。(可选)可以通过uppercase参数将包含字母字符的值设为大写。

{{randomValuelength=33type='ALPHANUMERIC'}}{{randomValuelength=12type='ALPHANUMERIC'uppercase=true}}{{randomValuelength=55type='ALPHABETIC'}}{{randomValuelength=27type='ALPHABETIC'uppercase=true}}{{randomValuelength=10type='NUMERIC'}}{{randomValuelength=5type='ALPHANUMERIC_AND_SYMBOLS'}}{{randomValuetype='UUID'}}

pickRandom

pickRandom辅助随机地从它的参数的值

如果第一个参数是一个集合,则将从该值中随机选择值:

// Assume that numberList = [1, 2, 3]{{pickRandomnumberList}} // One of 1, 2 or 3

否则将从提供的参数列表中选择一个值:

{{pickRandom'1''2''3'}} // One of 1, 2 or 3

4.2.7XML

XPath

XPATH用于从请求正文中提取值

例如,给定一个请求主体:


Stuff

以下将“ Stuff”呈现到输出中:

{{{xPathrequest.body'/outer/inner/text()'}}}

给定相同的XML,将呈现以下内容Stuff

{{{xPathrequest.body'/outer/inner'}}}

XPath还允许提取属性,例如针对以下请求主体:


id="123"/>

下面将在输出中呈现“ 123”:

{{{xPathrequest.body'/outer/inner/@id'}}}

遍历XML元素

例如,给定一个形式的请求正文:



One
Two
Three

和以下模板:
{{#each(xPathrequest.body'/stuff/thing')as|element|}}{{{element.text}}}{{/each}}

结果输出将是:

One Two Three

XML元素属性

xPath具有以下属性:

text:元素的文本内容。

name:元素的名称。

attributes:已选择属性名称和值的映射,例如给定XML元素:

id="123"position="top"/>

可以引用其属性:

      ID: {{{element.attributes.id}}}Position: {{{element.attributes.position}}}

4.2.8JSON

JsonPath

将使用JSONPath表达式语言从JSON文档中提取值,通过查询表达式选择单个值或子文档。

例如,给定JSON

{"outer":{"inner":"Stuff"}}

以下将“ Stuff”呈现到输出中:

{{jsonPathrequest.body'$.outer.inner'}}

对于相同的JSON,将呈现以下内容{ "inner": "Stuff" }:

{{jsonPathrequest.body'$.outer'}}

遍历JSON元素

例如,给定一个形式的请求正文:

{"things":[{"id":1},{"id":2},{"id":3}]}

以及以下响应正文模板:

{{#each(jsonPathrequest.body'$.things')as|thing|}}thing: {{{thing.id}}}{{/each}}

响应主体将包含:

thing: 1thing: 2thing: 3

又如,给定请求JSON:

{"things":{"one":1,"two":2,"three":3}}

和模板:

{{#each(jsonPathrequest.body'$.things')as|valuekey|}}{{{key}}}={{{value}}}{{/each}}

输出将包含:

one=1two=2three=3

JSON Web令牌(JWT)

jwtjwks都支持HS256(共享机密)和RS256(公钥/私钥)

生成令牌

可以通过启用模板并将以下内容简单地添加到respbse主体中来在存根响应中生成令牌:

{{{jwtmaxAge='12 days'}}}

到期日

可以通过设置maxAge参数自定义到期期限,例如

{{{jwtmaxAge='12 days'}}}

或通过设置绝对到期日期,例如

{{{jwtexp=(parseDate'2040-02-23T21:22:23Z')}}}

您可以类似地设置nbf(不早于)日期:

{{{jwtnbf=(parseDate'2018-02-23T21:22:23Z')}}}

其他

其他可参考帮助文档

https://www.mocklab.io/docs/response-templating/jwt/

4.3高级

4.3.1高级匹配

正文匹配

有必要根据帖子正文返回不同的响应(因为URL始终相同)

单击按钮添加子句,从下拉列表中选择匹配类型,然后将期望值或表达式写(或粘贴)到文本区域

 

请求优先级匹配

在列表中有多个存根映射与给定请求匹配的情况下,优先级较高的请求(即较低数量的请求)将首先匹配。

URL匹配

URL匹配包括以下:

MockLab:基于MockLab的第三方平台对接测试_第7张图片

高级请求参数匹配

除URL和正文外,请求还可以匹配:

  1. Header
  2. 查询参数
  3. Cookie

MockLab:基于MockLab的第三方平台对接测试_第8张图片 

添加多个match子句后,请求必须与所有子句都匹配才能送达响应(它们与逻辑AND结合在一起)

匹配JSON请求主体

JSON格式的请求主体存在两种特定的匹配类型:equalToJsonequals(matchesJsonPath)和JSONPath()。

匹配的XML请求主体

与JSON匹配一样,有两种可用于XML的匹配类型:equalToXml和matchesXPath

响应延迟

固定延迟

固定的延迟会在为响应提供服务之前直接增加指定毫秒数的暂停。

MockLab:基于MockLab的第三方平台对接测试_第9张图片

 

 

随机延迟

随机延迟会在提供响应之前添加随机暂停。有两种统计分布:

  • Uniform

MockLab:基于MockLab的第三方平台对接测试_第10张图片

  • Log normal

 MockLab:基于MockLab的第三方平台对接测试_第11张图片

  • Chunked dribble delay

MockLab:基于MockLab的第三方平台对接测试_第12张图片

Fault

实际的API和用于与它们进行通信的网络可能会以破坏您的应用程序的方式失败,并且难以测试。

MockLab支持使用四种不同的故障类型来响应请求:

  • 服务器在发送响应之前关闭连接
  • 发送损坏的数据,然后关闭连接
  • 发送OK响应,随后数据损坏并关闭连接
  • 对等连接重置-SO_LINGER设置为0会导致非正常的TCP连接终止。

Simulating Fault

MockLab:基于MockLab的第三方平台对接测试_第13张图片

 

Simulating FaultwithScenarios

假设有一个用于获取列表的“列表待办事项” API调用,则在上述测试期间必须两次调用该方法,第一次调用不返回任何项,第二次调用则新添加的项。由于这两个请求都是相同的(相同的URL,方法,请求标头),因此MockLab需要额外的内容来区分第一种情况和第二种情况。

例如:

  • 配置

创建一个空列表api,仅当名为“待办事项”的方案处于“已启动”状态时,该空api才匹配

MockLab:基于MockLab的第三方平台对接测试_第14张图片

 

然后创建一个api来处理第一个列表项的发布。触发时,此存根会将方案状态移至“添加的第一项”:

MockLab:基于MockLab的第三方平台对接测试_第15张图片

 

最后,创建一个存根以返回包含一个项目的列表,只有当场景处于“添加的第一项目”状态时,该列表才被匹配:

MockLab:基于MockLab的第三方平台对接测试_第16张图片

 

  • 测试

首先,GET请求获取列表,该列表应该为空。您应该可以执行任意次此操作,而不会改变结果:

$ curl http://example.mocklab.io/todo-items{  "items": []}

现在POST是一个新项(实际上,请求主体包含什么都没有关系,因为我们没有在存根中指定主体匹配器):

$ curl http://example.mocklab.io/todo-items -X POST

现在,这应该已经将方案状态移动到“添加的第一项”。现在再次获取项目列表应该返回一个项目:

$ curl http://example.mocklab.io/todo-items{  "items": [    {      "id": "1",      "description": "Read all about Scenarios"    }  ]}
  • 方案重置

单击可以将所有方案重置为“已启动”状态

实际示例

背景:物流对接物润时测试物润返回异常

创建并配置api

MockLab:基于MockLab的第三方平台对接测试_第17张图片

 

物流测试环境的appllo配置

MockLab:基于MockLab的第三方平台对接测试_第18张图片

 

触发上传后,会根据模拟桩的配置返回响应,具体响应因特殊情况在Rancher中查看日志

你可能感兴趣的:(测试工具篇,前端)