眾所周知,在進行接口測試的過程中,需要創建不同的場景(不同條件的輸入,來驗證不同的入參的返回結果)。因而,在日常的自動化接口監控或商品監控等線上監控過程中,需要配置大量的入參來監控接口的返回是否正確。
日常常見的線上監控幾個簡單的監控示例場景如下:
- 監控電商網站某個類目下的商品數量。若類目中商品的數量小於一定的數量,則認為需要認為查看商品池的商品是否正確;
- 監控商品的價格。當商品價格出現超出限定的波動幅度時,通知相應的商品負責人,對其進行確認,從而保證商品價格的正確無誤。
- 監控商品在某一地域是否有貨。當地域存貨達到最低庫存時,通知負責人進行貨源補充;或者當某地域限制出售或未配置地域庫存時,若發現存在相應庫存,則可通知相關人員查驗等。
那么,在 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):
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 請求的線程名稍作修改,如下所示:
再次執行腳本,執行結果如下所示,通過下圖是否可以非常直觀簡潔的執行結果,及對應的入參了 ^_^
附錄(一):

1 <?xml version="1.0" encoding="UTF-8"?> 2 <jmeterTestPlan version="1.2" properties="2.7" jmeter="2.12 r1636949"> 3 <hashTree> 4 <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="接口自動化測試實例 - 參數化" enabled="true"> 5 <stringProp name="TestPlan.comments"></stringProp> 6 <boolProp name="TestPlan.functional_mode">false</boolProp> 7 <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> 8 <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用戶定義的變量" enabled="true"> 9 <collectionProp name="Arguments.arguments"/> 10 </elementProp> 11 <stringProp name="TestPlan.user_define_classpath"></stringProp> 12 </TestPlan> 13 <hashTree> 14 <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="008-搜索商品" enabled="true"> 15 <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> 16 <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循環控制器" enabled="true"> 17 <boolProp name="LoopController.continue_forever">false</boolProp> 18 <stringProp name="LoopController.loops">3</stringProp> 19 </elementProp> 20 <stringProp name="ThreadGroup.num_threads">3</stringProp> 21 <stringProp name="ThreadGroup.ramp_time">0</stringProp> 22 <longProp name="ThreadGroup.start_time">1419232321000</longProp> 23 <longProp name="ThreadGroup.end_time">1419232321000</longProp> 24 <boolProp name="ThreadGroup.scheduler">false</boolProp> 25 <stringProp name="ThreadGroup.duration"></stringProp> 26 <stringProp name="ThreadGroup.delay"></stringProp> 27 </ThreadGroup> 28 <hashTree> 29 <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="參數化列表文件 - 待搜索的商品列表" enabled="true"> 30 <stringProp name="filename">./mobileApp-IDC-0-searchGoods.txt</stringProp> 31 <stringProp name="fileEncoding">UTF-8</stringProp> 32 <stringProp name="variableNames">goodsNo,districtId,areacode,sysNo,skuid,appSource,appVersion</stringProp> 33 <stringProp name="delimiter">,</stringProp> 34 <boolProp name="quotedData">false</boolProp> 35 <boolProp name="recycle">true</boolProp> 36 <boolProp name="stopThread">false</boolProp> 37 <stringProp name="shareMode">shareMode.all</stringProp> 38 </CSVDataSet> 39 <hashTree/> 40 <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="008-01-搜索商品 【${goodsNo} - ${districtId} - ${appSource} - ${appVersion}】" enabled="true"> 41 <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> 42 <collectionProp name="Arguments.arguments"> 43 <elementProp name="q" elementType="HTTPArgument"> 44 <boolProp name="HTTPArgument.always_encode">false</boolProp> 45 <stringProp name="Argument.value">${goodsNo}</stringProp> 46 <stringProp name="Argument.metadata">=</stringProp> 47 <boolProp name="HTTPArgument.use_equals">true</boolProp> 48 <stringProp name="Argument.name">q</stringProp> 49 </elementProp> 50 <elementProp name="districtId" elementType="HTTPArgument"> 51 <boolProp name="HTTPArgument.always_encode">false</boolProp> 52 <stringProp name="Argument.value">${districtId}</stringProp> 53 <stringProp name="Argument.metadata">=</stringProp> 54 <boolProp name="HTTPArgument.use_equals">true</boolProp> 55 <stringProp name="Argument.name">districtId</stringProp> 56 </elementProp> 57 <elementProp name="areacode" elementType="HTTPArgument"> 58 <boolProp name="HTTPArgument.always_encode">false</boolProp> 59 <stringProp name="Argument.value">${areacode}</stringProp> 60 <stringProp name="Argument.metadata">=</stringProp> 61 <boolProp name="HTTPArgument.use_equals">true</boolProp> 62 <stringProp name="Argument.name">areacode</stringProp> 63 </elementProp> 64 <elementProp name="dtype" elementType="HTTPArgument"> 65 <boolProp name="HTTPArgument.always_encode">false</boolProp> 66 <stringProp name="Argument.value">list|page|classes</stringProp> 67 <stringProp name="Argument.metadata">=</stringProp> 68 <boolProp name="HTTPArgument.use_equals">true</boolProp> 69 <stringProp name="Argument.name">dtype</stringProp> 70 </elementProp> 71 <elementProp name="districtId" elementType="HTTPArgument"> 72 <boolProp name="HTTPArgument.always_encode">false</boolProp> 73 <stringProp name="Argument.name">districtId</stringProp> 74 <stringProp name="Argument.value">${districtId}</stringProp> 75 <stringProp name="Argument.metadata">=</stringProp> 76 <boolProp name="HTTPArgument.use_equals">true</boolProp> 77 </elementProp> 78 <elementProp name="exAppTag" elementType="HTTPArgument"> 79 <boolProp name="HTTPArgument.always_encode">false</boolProp> 80 <stringProp name="Argument.name">exAppTag</stringProp> 81 <stringProp name="Argument.value">2045191607</stringProp> 82 <stringProp name="Argument.metadata">=</stringProp> 83 <boolProp name="HTTPArgument.use_equals">true</boolProp> 84 </elementProp> 85 <elementProp name="appSource" elementType="HTTPArgument"> 86 <boolProp name="HTTPArgument.always_encode">false</boolProp> 87 <stringProp name="Argument.value">${appSource}</stringProp> 88 <stringProp name="Argument.metadata">=</stringProp> 89 <boolProp name="HTTPArgument.use_equals">true</boolProp> 90 <stringProp name="Argument.name">appSource</stringProp> 91 </elementProp> 92 <elementProp name="appVersion" elementType="HTTPArgument"> 93 <boolProp name="HTTPArgument.always_encode">false</boolProp> 94 <stringProp name="Argument.value">${appVersion}</stringProp> 95 <stringProp name="Argument.metadata">=</stringProp> 96 <boolProp name="HTTPArgument.use_equals">true</boolProp> 97 <stringProp name="Argument.name">appVersion</stringProp> 98 </elementProp> 99 </collectionProp> 100 </elementProp> 101 <stringProp name="HTTPSampler.domain">mb.51buy.com</stringProp> 102 <stringProp name="HTTPSampler.port"></stringProp> 103 <stringProp name="HTTPSampler.connect_timeout"></stringProp> 104 <stringProp name="HTTPSampler.response_timeout"></stringProp> 105 <stringProp name="HTTPSampler.protocol">http</stringProp> 106 <stringProp name="HTTPSampler.contentEncoding">GB2312</stringProp> 107 <stringProp name="HTTPSampler.path">/json.php?mod=Search&act=page&p=1&pp=20</stringProp> 108 <stringProp name="HTTPSampler.method">POST</stringProp> 109 <boolProp name="HTTPSampler.follow_redirects">true</boolProp> 110 <boolProp name="HTTPSampler.auto_redirects">false</boolProp> 111 <boolProp name="HTTPSampler.use_keepalive">true</boolProp> 112 <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> 113 <boolProp name="HTTPSampler.monitor">false</boolProp> 114 <stringProp name="HTTPSampler.embedded_url_re"></stringProp> 115 </HTTPSamplerProxy> 116 <hashTree> 117 <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息頭管理器" enabled="true"> 118 <collectionProp name="HeaderManager.headers"> 119 <elementProp name="Charset" elementType="Header"> 120 <stringProp name="Header.name">Charset</stringProp> 121 <stringProp name="Header.value">UTF-8</stringProp> 122 </elementProp> 123 <elementProp name="Content-Type" elementType="Header"> 124 <stringProp name="Header.name">Content-Type</stringProp> 125 <stringProp name="Header.value">application/x-www-form-urlencoded</stringProp> 126 </elementProp> 127 <elementProp name="Accept-Encoding" elementType="Header"> 128 <stringProp name="Header.name">Accept-Encoding</stringProp> 129 <stringProp name="Header.value">gzip</stringProp> 130 </elementProp> 131 <elementProp name="User-Agent" elementType="Header"> 132 <stringProp name="Header.name">User-Agent</stringProp> 133 <stringProp name="Header.value">Dalvik/1.6.0 (Linux; U; Android 4.4.2; GT-I9502 Build/KOT49H)</stringProp> 134 </elementProp> 135 </collectionProp> 136 </HeaderManager> 137 <hashTree/> 138 <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="響應斷言" enabled="true"> 139 <collectionProp name="Asserion.test_strings"> 140 <stringProp name="365072765">${goodsNo}</stringProp> 141 <stringProp name="-781931418">${sysNo}</stringProp> 142 <stringProp name="-794775524">${skuid}</stringProp> 143 </collectionProp> 144 <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> 145 <boolProp name="Assertion.assume_success">false</boolProp> 146 <intProp name="Assertion.test_type">16</intProp> 147 </ResponseAssertion> 148 <hashTree/> 149 </hashTree> 150 </hashTree> 151 <ResultCollector guiclass="AssertionVisualizer" testclass="ResultCollector" testname="斷言結果" enabled="true"> 152 <boolProp name="ResultCollector.error_logging">false</boolProp> 153 <objProp> 154 <name>saveConfig</name> 155 <value class="SampleSaveConfiguration"> 156 <time>true</time> 157 <latency>true</latency> 158 <timestamp>true</timestamp> 159 <success>true</success> 160 <label>true</label> 161 <code>true</code> 162 <message>true</message> 163 <threadName>true</threadName> 164 <dataType>true</dataType> 165 <encoding>false</encoding> 166 <assertions>true</assertions> 167 <subresults>true</subresults> 168 <responseData>false</responseData> 169 <samplerData>false</samplerData> 170 <xml>false</xml> 171 <fieldNames>false</fieldNames> 172 <responseHeaders>false</responseHeaders> 173 <requestHeaders>false</requestHeaders> 174 <responseDataOnError>false</responseDataOnError> 175 <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage> 176 <assertionsResultsToSave>0</assertionsResultsToSave> 177 <bytes>true</bytes> 178 <threadCounts>true</threadCounts> 179 </value> 180 </objProp> 181 <stringProp name="filename"></stringProp> 182 </ResultCollector> 183 <hashTree/> 184 <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看結果樹" enabled="true"> 185 <boolProp name="ResultCollector.error_logging">false</boolProp> 186 <objProp> 187 <name>saveConfig</name> 188 <value class="SampleSaveConfiguration"> 189 <time>true</time> 190 <latency>true</latency> 191 <timestamp>true</timestamp> 192 <success>true</success> 193 <label>true</label> 194 <code>true</code> 195 <message>true</message> 196 <threadName>true</threadName> 197 <dataType>true</dataType> 198 <encoding>false</encoding> 199 <assertions>true</assertions> 200 <subresults>true</subresults> 201 <responseData>false</responseData> 202 <samplerData>false</samplerData> 203 <xml>false</xml> 204 <fieldNames>false</fieldNames> 205 <responseHeaders>false</responseHeaders> 206 <requestHeaders>false</requestHeaders> 207 <responseDataOnError>false</responseDataOnError> 208 <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage> 209 <assertionsResultsToSave>0</assertionsResultsToSave> 210 <bytes>true</bytes> 211 <threadCounts>true</threadCounts> 212 </value> 213 </objProp> 214 <stringProp name="filename">${__property(JmeterAuto_LogFile)}</stringProp> 215 </ResultCollector> 216 <hashTree/> 217 </hashTree> 218 </hashTree> 219 </jmeterTestPlan>
至此, JMeter學習-010-JMeter 配置元件實例之 - CSV Data Set Config 參數化配置 順利完結,希望此文能夠給初學 JMeter 的您一份參考。
最后,非常感謝親的駐足,希望此文能對親有所幫助。熱烈歡迎親一起探討,共同進步。非常感謝! ^_^