(8 | JMeter关联)

1 测试对象

  • 接之前的说明,我们的测试对象为禅道开源版本;
  • 按照之前的文章搭建部署好本地禅道,开启服务即可

①先到官网下载Windows 一键安装包,安装完后启动服务即可; ②直接使用官网的《禅道API文档V1》,地址为:https://www.zentao.net/book/apidoc-v1/664.html

在这里插入图片描述

  • 涉及的接口数据:

在这里插入图片描述 在这里插入图片描述

2 Jmeter关联

2.1 定义

  • 简单理解为某个接口的数据,需要依赖另一个接口的返回值
  • 这里我们举例说明,比如接口“获取我的个人信息”接口,需要依赖token;那么就需要先进行token获取,再加入请求头中;
  • 以上操作要满足,需要用到JMeter的【后置处理器】来进行操作。

2.2 后置处理器

  • 即为JMeter的关联元件;
  • 可帮助我们从服务器接口返回值中查找我们想要的数据;
  • 以下分别通过【JSON提取器】和【正则表达式提取器】来获取token值,用于后续接口的关联数据。

2.3 JSON提取器

2.3.1 新建线程组

  • 在【测试计划】下新建一个线程组,名为【禅道接口】,并从【配置元件】中添加【HTTP信息头管理器】

在这里插入图片描述

  • 【HTTP信息头管理器】中是我们需要请求的头信息,这里按照禅道接口文档,只指定了请求数据的格式为:application/json

在这里插入图片描述

2.3.2 新建取样器获取token接口

  • 在线程组【禅道接口】下添加【取样器】-【HTTP请求】,名为1-获取token;

在这里插入图片描述

  • 按照接口文档,填写请求数据,注意以后的ip、port都在测试计划中进行声明为用户全局变量;

在这里插入图片描述

2.3.3 添加【JSON提取器】

  • 在http请求【1-获取token】中添加【后置处理器】-【JSON提取器】,并设置提取的参数,这里我们需要欧聪获取token的接口中提取token的值;
  • 提取规则如下:

在这里插入图片描述

在这里插入图片描述

2.3.4 引用参数

  • 新建第二个接口“2-用户-1获取我的个人信息”,填写接口的请求信息: 在这里插入图片描述
  • 在该接口下添加一个【HTTP信息头管理器】,并引用token参数:

在这里插入图片描述

2.3.5 添加查看结果树

  • 测试计划-【监听器】-【查看结果树】;
  • 运行JMeter,数据请求成功,如下:

在这里插入图片描述

2.3.6 如何判断应用是否成功?

  • 我们可以禁用应用token,看是否接口“2-用户-1获取我的个人信息”能请求成功;
  • 禁用提取的token后,再次运行,提示没有进行认证,那就说明关联是OK了。 在这里插入图片描述

2.4 正则表达式提取

2.4.1 正则表达式提取器

  • 禁用之前的【JSON提取器】,添加【正则表达式提取器】,添加规则如下: 在这里插入图片描述

  • 按照获取token的返回值,提取测试如下: 在这里插入图片描述

  • 其他运行方式和参数引用和【JOSN提取器】一样。

2.4.2 界面参数说明

在这里插入图片描述

2.4.2.1 apply to
参数 说明
Main sample and sub-samples 作用于父节点取样器及对应子节点取样器
Main sample only 仅作用于父节点取样器
Sub-samples only 仅作用于子节点取样器
JMeter Variable 作用于jmeter变量(输入框内可输入jmeter的变量名称)
2.4.2.2 Field to check
参数 说明
Body 接口响应主体内容,默认选择此项
Response Headers 响应头的所有内容
Request Headers 请求头的所有内容
url 是对sample的url进行匹配
Response Code http响应代码
Response Message http响应代码对应的响应信息
2.4.2.3 name of created Variable
  • 自定义变量名,其他地方引用时可填写;
  • 例如${变量名}
2.4.2.4 Regular Expression
  • 正则表达式;
  • 语法为:左边界(.*?)右边界,左右边界就是为了能准确定位到想匹配的内容
  • 规则:

在这里插入图片描述

.*:贪婪匹配原则:整个表达式匹配成功的前提下,尽可能多的匹配,即匹配到不能匹配为止
.*?:非贪婪匹配:在匹配成功的情况下尽可能少的匹配
2.4.2.5 Template
  • 模板是对应正则表达式提取器类型,样式为:$n$
若为:$0$,则为整个表达式匹配到的内容,就是包括小括号内跟小括号外的内容
若为:$1$,则对应正则表达式中的第一个(.*?)所匹配的内容
若为:$2$,则对应正则表达式中的第二个(.*?)所匹配的内容
若为:$1$$2$,则把2个(.*?)所匹配的内容拼接起来
2.4.2.5 Match No.
  • 正则表达式匹配数据的最终结果可以看做一个数组,匹配数字即可看做是数组的第几个元素;
  • 当为 0 时,随机返回匹配的数据;为 1 时,表示返回匹配结果数组的第一个元素;
  • 当为负数(-1,-2,-100都可以)时,表示返回全部元素,并且同时会返回一个元素总数的变量,引用时,通过${变量名_1}的方式来取第1个匹配的内容,${变量名_2}来取第2个匹配的内容
2.4.2.6 Default Value
  • 匹配失败时的默认值。通常用于后续的逻辑判断,建议使用一些特殊含义的,比如0,NULL,ERROR等。

3 本文涉及的脚本

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.3">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
        <collectionProp name="Arguments.arguments">
          <elementProp name="IP" elementType="Argument">
            <stringProp name="Argument.name">IP</stringProp>
            <stringProp name="Argument.value">127.0.0.1</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="PORT" elementType="Argument">
            <stringProp name="Argument.name">PORT</stringProp>
            <stringProp name="Argument.value">80</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="COMMON" elementType="Argument">
            <stringProp name="Argument.name">COMMON</stringProp>
            <stringProp name="Argument.value">/zentao/api.php/v1</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
        </collectionProp>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">
        <collectionProp name="HeaderManager.headers">
          <elementProp name="" elementType="Header">
            <stringProp name="Header.name">Content-Type</stringProp>
            <stringProp name="Header.value">application/json</stringProp>
          </elementProp>
        </collectionProp>
      </HeaderManager>
      <hashTree/>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="禅道接口" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="1-获取token" enabled="true">
          <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments">
              <elementProp name="" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">{&quot;account&quot;: &quot;admin&quot;, &quot;password&quot;: &quot;123456&quot;}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${IP}</stringProp>
          <stringProp name="HTTPSampler.port">${PORT}</stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path">${COMMON}/tokens</stringProp>
          <stringProp name="HTTPSampler.method">POST</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON提取器-提取返回的token" enabled="false">
            <stringProp name="JSONPostProcessor.referenceNames">token</stringProp>
            <stringProp name="JSONPostProcessor.jsonPathExprs">$.token</stringProp>
            <stringProp name="JSONPostProcessor.match_numbers">1</stringProp>
            <stringProp name="JSONPostProcessor.defaultValues">无</stringProp>
          </JSONPostProcessor>
          <hashTree/>
          <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="正则表达式提取器" enabled="true">
            <stringProp name="RegexExtractor.useHeaders">false</stringProp>
            <stringProp name="RegexExtractor.refname">token</stringProp>
            <stringProp name="RegexExtractor.regex">&quot;token&quot;:&quot;(.*?)&quot;</stringProp>
            <stringProp name="RegexExtractor.template">$1$</stringProp>
            <stringProp name="RegexExtractor.default"></stringProp>
            <stringProp name="RegexExtractor.match_number">1</stringProp>
          </RegexExtractor>
          <hashTree/>
        </hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="2-用户-1获取我的个人信息" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${IP}</stringProp>
          <stringProp name="HTTPSampler.port">${PORT}</stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path">${COMMON}/user</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="保持登陆会话" enabled="true">
            <collectionProp name="HeaderManager.headers">
              <elementProp name="" elementType="Header">
                <stringProp name="Header.name">token</stringProp>
                <stringProp name="Header.value">${token}</stringProp>
              </elementProp>
            </collectionProp>
          </HeaderManager>
          <hashTree/>
        </hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="2-用户-2获取用户列表" enabled="true">
          <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments">
              <elementProp name="" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">{&#xd;
    &quot;page&quot;: 1,&#xd;
    &quot;limit&quot;: 1&#xd;
}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${IP}</stringProp>
          <stringProp name="HTTPSampler.port">${PORT}</stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path">${COMMON}/users</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="保持登陆会话" enabled="true">
            <collectionProp name="HeaderManager.headers">
              <elementProp name="" elementType="Header">
                <stringProp name="Header.name">token</stringProp>
                <stringProp name="Header.value">${token}</stringProp>
              </elementProp>
            </collectionProp>
          </HeaderManager>
          <hashTree/>
        </hashTree>
      </hashTree>
      <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" enabled="true">
        <boolProp name="ResultCollector.error_logging">false</boolProp>
        <objProp>
          <name>saveConfig</name>
          <value class="SampleSaveConfiguration">
            <time>true</time>
            <latency>true</latency>
            <timestamp>true</timestamp>
            <success>true</success>
            <label>true</label>
            <code>true</code>
            <message>true</message>
            <threadName>true</threadName>
            <dataType>true</dataType>
            <encoding>false</encoding>
            <assertions>true</assertions>
            <subresults>true</subresults>
            <responseData>false</responseData>
            <samplerData>false</samplerData>
            <xml>false</xml>
            <fieldNames>true</fieldNames>
            <responseHeaders>false</responseHeaders>
            <requestHeaders>false</requestHeaders>
            <responseDataOnError>false</responseDataOnError>
            <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
            <assertionsResultsToSave>0</assertionsResultsToSave>
            <bytes>true</bytes>
            <sentBytes>true</sentBytes>
            <url>true</url>
            <threadCounts>true</threadCounts>
            <idleTime>true</idleTime>
            <connectTime>true</connectTime>
          </value>
        </objProp>
        <stringProp name="filename"></stringProp>
      </ResultCollector>
      <hashTree/>
    </hashTree>
  </hashTree>
</jmeterTestPlan>