httprunner如何提取數據串聯上下游接口


httprunner進行接口測試時,從上一個接口提取參數傳遞給下游接口,如何獲取數據里最后一個值?

突然被學員問道一個httprunner的問題,慚愧的是大貓之前沒有是通過httprunner,又不好意思說不會,只能硬着頭皮去看源碼了。

 

問題其實很簡單,怎么處理我不管,反正你得給答案。看一眼同學反饋的截圖,確實不難,問題很簡單。請求某一個接口,接口返回的content里包含多個字段,需要獲取到最后一個字典里的數據。是不是覺得很簡單?

 

 

對於這么具體的問題,大貓當然是第一反應去百度啦!當然,如果能簡單百度到答案,學員也不會來問我,因此,結果可想而知,百度沒有標准答案!

 

 

不過百度一點用處也沒有么,也不盡然,至少對於一只從來沒有使用過httprunner的大貓來說,知道從響應提取數據使用extract關鍵字。

既然百度沒有標准答案,我們就代碼里找,大貓最不怕的就是看代碼,大江大浪都走過來了,還能這幾千行代碼里翻船?

看代碼先要去github把代碼拉取到本地(這里就不寫怎么做了),用pycharm打開,然后使用pycharm的“find in path...”進行全局查找,像這樣:

我們點擊去看下代碼的實現細節,沒准可以發現蛛絲馬跡。

if not extractors:
return {}

logger.log_debug("start to extract from response object.")
extracted_variables_mapping = OrderedDict()
extract_binds_order_dict = utils.ensure_mapping_format(extractors)

for key, field in extract_binds_order_dict.items():
extracted_variables_mapping[key] = self.extract_field(field)

return extracted_variables_mapping

代碼實現相當簡潔,實例化一個OrderedDict用於存儲提取后的數據,采用extract_field函數來執行具體的提取數據操作。我們接着看extract_field函數。

text_extractor_regexp_compile = re.compile(r".*\(.*\).*")

if text_extractor_regexp_compile.match(field):
value = self._extract_field_with_regex(field)
else:
value = self._extract_field_with_delimiter(field)

extract_field的核心邏輯也非常簡潔,采用re.compile判斷表達式是否為正則,是的話執行正則表達式提取_extract_field_with_regex,如果不是正則采用分隔符提取方式,_extract_field_with_delimiter,我們需要的是分隔符方式提取,因此看_extract_field_with_delimiter函數的實現。

_extract_field_with_delimiter函數實現略微復雜,函數里對查詢字符串進行了分級處理,content.person.name.first_name被分成top_query:content和sub_query:[person, name, first_name]。

同時不同的top_query有不同的處理方法,例如top_query若是status_code,encoding,ok,reason,url等則不能有sub_query,否則會拋出異常(這里和響應對象結構有關系);

if top_query in ["status_code", "encoding", "ok", "reason", "url"]:
if sub_query:
# status_code.XX
err_msg = u"Failed to extract: {}\n".format(field)
logger.log_error(err_msg)
raise exceptions.ParamsError(err_msg)

return getattr(self, top_query)

對於top_query是cookies和headers的處理,若存在sub_query則以sub_query為key進行取值,否則返回cookie或header整體。

elif top_query == "cookies":
cookies = self.cookies
if not sub_query:
# extract cookies
return cookies

try:
return cookies[sub_query]
except KeyError:
err_msg = u"Failed to extract cookie! => {}\n".format(field)
err_msg += u"response cookies: {}\n".format(cookies)
logger.log_error(err_msg)
raise exceptions.ExtractFailure(err_msg)

如果top_query是content,text或json則使用query_json函數進一步處理。當然,處理前進行了一次判斷,sub_query是字典、列表還是數字。

elif top_query in ["content", "text", "json"]:
try:
body = self.json
except exceptions.JSONDecodeError:
body = self.text

if not sub_query:
# extract response body
return body

if isinstance(body, (dict, list)):
# content = {"xxx": 123}, content.xxx
return utils.query_json(body, sub_query)
elif sub_query.isdigit():
# content = "abcdefg", content.3 => d
return utils.query_json(body, sub_query)
else:
# content = "<html>abcdefg</html>", content.xxx
err_msg = u"Failed to extract attribute from response body! => {}\n".format(field)
err_msg += u"response body: {}\n".format(body)
logger.log_error(err_msg)
raise exceptions.ExtractFailure(err_msg)

query_json函數是實現取值的關鍵,函數使用分隔符講sub_query切分成字符串數據,采用循環遍歷數據。

for key in query.split(delimiter):
if isinstance(json_content, (list, basestring)):
json_content = json_content[int(key)]    <- 這里是關鍵
elif isinstance(json_content, dict):
json_content = json_content[key]
else:
logger.log_error(
"invalid type value: {}({})".format(json_content, type(json_content)))
raise_flag = True

這里如果數據是列表則將key轉化為數字取值,否則認為是字典直接用key取值。既然代碼是轉化成整形,整數有正整數,負整數和零,理論上提取數據的字符串可以有content.person.-1.name.first_name

這樣的存在。這里可以理解為從content里取最后一個person的name的first_name。

 

另外,這里多說一句,可以看見httprunner的代碼並不難python測開的學員應該都能看到懂,希望大家多看看開源框架的代碼,提升自己的代碼能力,也希望大家報名我的課程。

作  者:Testfan 大貓

出  處:微信公眾號:自動化軟件測試平台

版權說明:歡迎轉載,但必須注明出處,並在文章頁面明顯位置給出文章鏈接

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM