JMeter学习-010-JMeter 配置元件实例之 - CSV Data Set Config 参数化配置

众所周知,在进行接口测试的过程中,需要创建不同的场景(不同条件的输入,来验证不同的入参的返回结果)。因而,在日常的自动化接口监控或商品监控等线上监控过程中,需要配置大量的入参来监控接口的返回是否正确。

日常常见的线上监控几个简单的监控示例场景如下:

  • 监控电商网站某个类目下的商品数量。若类目中商品的数量小于一定的数量,则认为需要认为查看商品池的商品是否正确;
  • 监控商品的价格。当商品价格出现超出限定的波动幅度时,通知相应的商品负责人,对其进行确认,从而保证商品价格的正确无误。
  • 监控商品在某一地域是否有货。当地域存货达到最低库存时,通知负责人进行货源补充;或者当某地域限制出售或未配置地域库存时,若发现存在相应库存,则可通知相关人员查验等。

那么,在 JMeter 中改如何对 HTTP请求进行参数化呢?

在 JMeter 的配置原件中就为我们提供了参数化需要的配置,下面以 CSV Data Set Config 进行参数化演示示例的配置,路径如下图所示:

PS:通过上图可以看出我们可以进行各种各样的参数化设置,例如用户自定义变量、计数器、HTTP Cookie 管理器等,同时也可以通过 JDBC 读取数据库中保存的测试数据(需要相应的 jar 文件支持)。

CSV Data Set Config 读取的文件不仅仅限于 csv 文件,其可以读取文本文件,文件内容需以英文半角 "," 隔开,每一行为一条测试用例数据。

下面开始以大家都熟悉的搜索功能展开 HTTP请求 参数化配置实例演示,具体的操作步骤如下所示:

第一步、获取搜索的 HTTP请求

获取搜素的 HTTP请求如下图所示(此步不再赘述,不知如何获取 HTTP请求 的小主,敬请参阅之前的博文,非常感谢!):

第二步、通过 HTTP请求,确定需要参数化的变量

通常根据实际的参数化需求,选取符合测试需求且经常变化或未来会变化的变量为需要参数化的变量,例如本例中的查询关键字、配送区域、大区、产品源(或产品平台,例如:PC、Android、iPhone等)、产品版本,均可作为参数化的变量。

第三步、创建参数化文件,并填充测试用例数据

创建参数文件 mobileApp-IDC-0-searchGoods.txt,参数文件内容如下所示(注意参数文件编码方式建议选择 UTF-8):

1

2

3

4

5

6

7

101-006-78945,29313,1,2169256,489349159815,android,45

101-005-31058,29313,1,1903913,472108827742,android,45

101-005-98536,29357,1,1993575,477948510289,android,45

101-006-88137,29358,1,2184120,490316513802,iphone,45

101-006-99103,29358,1,2201898,491473066849,iphone,45

101-005-84629,29358,1,1978716,476978170298,android,32

101-006-25777,29358,1,2035074,480647572701,android,45

第四步、通过 【配置元件/CSV Data Set Config】读取参数化文件,并对变量进行赋值。

最终的配置如下图所示:

  • Filename:参数文件名,可以写绝对路径,个人强烈建议采用相对路径,避免脚本迁移时需要修改路径。
  • File encoding:参数文件的编码格式。推荐选择 UTF-8。
  • Variable Names:对对应参数文件每列的变量名。类似于 Excel 文件的文件头,起到标示的作用,同时也是后续引用的标识符,建议采用有意义的英文标示。
  • Delimiter:参数文件分隔符。与参数文件中的分隔符保持一致即可。
  • Allow quoted data?:是否允许引用数据。默认设置为 false。例如数据样式为:"101-005-98536","29357","1","1993575","477948510289","android","45" 时,此处需设置为 true,一般默认为 false 即可。
  • Recycle on EOF?:是否循环读取参数文件内容。默认设置为 true。设置为 true 时,当已经读取完参数文件内的测试用例数据,还需要继续获取用例数据时,此时会循环读取参数文件数据;设置为 false 时,若已至文件末尾,则不再继续读取测试数据。通常在 线程组的线程数 * 线程组的循环次数 > 参数文件行数时,才需要将此项设置为 true。
  • Sotp thread on EOF?:当读取到参数文件末尾时,是否停止读取线程。默认为 false。当 Recycle on EOF?  设置为 true 时,此项不起任何作用。当且仅当 Recycle on EOF? 为 false 时,此项配置才生效。
  • 若为 true,则在读取到参数文件行末尾时,终止参数文件读取线程。例如:线程组的线程数 * 线程组的循环次数 = 10,参数文件行数 = 7,那么将在第 8 次开始停止线程。
  • 若为 false,此时线程会继续读取,但是会请求错误,因此时读取的数据为 EOF。以上同例,自第 8 次开始,线程的请求数据为 EOF。
  • Sharing mode:共享模式,即参数文件变量作用域。主要有以下几种方式:
  • All threads:当前测试计划中的所有线程组中的所有的线程均有效。默认。
  • Current thread group:当前的线程组中的线程有效。
  • Current thread:当前线程有效。
  • 编辑:(俺暂时没有用过,嘿嘿 ^_^)

第五步、修改 HTTP请求 中对应的参数为参数变量

将如下部分中值替换为参数文件的值,即对应的 Variable Names,应用方式类似于 shell 脚本的变量引用,例如:${goodsNo}。

第六步、设置 HTTP请求 断言

将参数文件中的第 5 列(即商品的 skuid 设置为校验数据,通过此判断单品是否正确),测试数据如下所示:

设置断言如下所示:

第七步:执行脚本,查看结果

参数文件用例数据 101-006-25777,29358,1,2035074,480647572731,android,45 对应的 skuid:480647572731 设置的是错误的(方便对比断言结果)。执行结果如下所示:

第八步:脚本改进

其实,有些朋友看到这儿,可能会问执行结果列的标题均为线程标题,都是相同的无法区分,不方便阅读?其实,我们可以将脚本中的变量添加到线程名的后面(PS:需要考虑变量的作用域)即可简洁明了的区分每个线程,同时也可查看不同的入参,非常的方便。

将上述 HTTP 请求的线程名稍作修改,如下所示:

再次执行脚本,执行结果如下所示,通过下图是否可以非常直观简洁的执行结果,及对应的入参了 ^_^

附录(一):

 JMeter 配置元件 - CSV Data Set Config 参数化演示实例脚本源码

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.7" jmeter="2.12 r1636949">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="接口自动化测试实例 - 参数化" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</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>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="008-搜索商品" 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">3</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">3</stringProp>
        <stringProp name="ThreadGroup.ramp_time">0</stringProp>
        <longProp name="ThreadGroup.start_time">1419232321000</longProp>
        <longProp name="ThreadGroup.end_time">1419232321000</longProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>
      <hashTree>
        <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="参数化列表文件 - 待搜索的商品列表" enabled="true">
          <stringProp name="filename">./mobileApp-IDC-0-searchGoods.txt</stringProp>
          <stringProp name="fileEncoding">UTF-8</stringProp>
          <stringProp name="variableNames">goodsNo,districtId,areacode,sysNo,skuid,appSource,appVersion</stringProp>
          <stringProp name="delimiter">,</stringProp>
          <boolProp name="quotedData">false</boolProp>
          <boolProp name="recycle">true</boolProp>
          <boolProp name="stopThread">false</boolProp>
          <stringProp name="shareMode">shareMode.all</stringProp>
        </CSVDataSet>
        <hashTree/>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="008-01-搜索商品 【${goodsNo} - ${districtId} - ${appSource} - ${appVersion}】" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
            <collectionProp name="Arguments.arguments">
              <elementProp name="q" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">${goodsNo}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">q</stringProp>
              </elementProp>
              <elementProp name="districtId" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">${districtId}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">districtId</stringProp>
              </elementProp>
              <elementProp name="areacode" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">${areacode}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">areacode</stringProp>
              </elementProp>
              <elementProp name="dtype" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">list|page|classes</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">dtype</stringProp>
              </elementProp>
              <elementProp name="districtId" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.name">districtId</stringProp>
                <stringProp name="Argument.value">${districtId}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
              </elementProp>
              <elementProp name="exAppTag" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.name">exAppTag</stringProp>
                <stringProp name="Argument.value">2045191607</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
              </elementProp>
              <elementProp name="appSource" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">${appSource}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">appSource</stringProp>
              </elementProp>
              <elementProp name="appVersion" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">${appVersion}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">appVersion</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">mb.51buy.com</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.contentEncoding">GB2312</stringProp>
          <stringProp name="HTTPSampler.path">/json.php?mod=Search&amp;act=page&amp;p=1&amp;pp=20</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>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">
            <collectionProp name="HeaderManager.headers">
              <elementProp name="Charset" elementType="Header">
                <stringProp name="Header.name">Charset</stringProp>
                <stringProp name="Header.value">UTF-8</stringProp>
              </elementProp>
              <elementProp name="Content-Type" elementType="Header">
                <stringProp name="Header.name">Content-Type</stringProp>
                <stringProp name="Header.value">application/x-www-form-urlencoded</stringProp>
              </elementProp>
              <elementProp name="Accept-Encoding" elementType="Header">
                <stringProp name="Header.name">Accept-Encoding</stringProp>
                <stringProp name="Header.value">gzip</stringProp>
              </elementProp>
              <elementProp name="User-Agent" elementType="Header">
                <stringProp name="Header.name">User-Agent</stringProp>
                <stringProp name="Header.value">Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9502 Build/KOT49H)</stringProp>
              </elementProp>
            </collectionProp>
          </HeaderManager>
          <hashTree/>
          <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="true">
            <collectionProp name="Asserion.test_strings">
              <stringProp name="365072765">${goodsNo}</stringProp>
              <stringProp name="-781931418">${sysNo}</stringProp>
              <stringProp name="-794775524">${skuid}</stringProp>
            </collectionProp>
            <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
            <boolProp name="Assertion.assume_success">false</boolProp>
            <intProp name="Assertion.test_type">16</intProp>
          </ResponseAssertion>
          <hashTree/>
        </hashTree>
      </hashTree>
      <ResultCollector guiclass="AssertionVisualizer" 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>false</fieldNames>
            <responseHeaders>false</responseHeaders>
            <requestHeaders>false</requestHeaders>
            <responseDataOnError>false</responseDataOnError>
            <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
            <assertionsResultsToSave>0</assertionsResultsToSave>
            <bytes>true</bytes>
            <threadCounts>true</threadCounts>
          </value>
        </objProp>
        <stringProp name="filename"></stringProp>
      </ResultCollector>
      <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>false</fieldNames>
            <responseHeaders>false</responseHeaders>
            <requestHeaders>false</requestHeaders>
            <responseDataOnError>false</responseDataOnError>
            <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
            <assertionsResultsToSave>0</assertionsResultsToSave>
            <bytes>true</bytes>
            <threadCounts>true</threadCounts>
          </value>
        </objProp>
        <stringProp name="filename">${__property(JmeterAuto_LogFile)}</stringProp>
      </ResultCollector>
      <hashTree/>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

JMeter 配置元件 - CSV Data Set Config 参数化演示实例脚本源码

至此, JMeter学习-010-JMeter 配置元件实例之 - CSV Data Set Config 参数化配置 顺利完结,希望此文能够给初学 JMeter 的您一份参考。

最后,非常感谢亲的驻足,希望此文能对亲有所帮助。热烈欢迎亲一起探讨,共同进步。非常感谢! ^_^

发布了36 篇原创文章 · 获赞 3 · 访问量 6887

猜你喜欢

转载自blog.csdn.net/sun_hm/article/details/105045349