阶段三-02 用例之间上下文传递
context上下文,需要運用在接口執行里
參考jmeter后置處理器來設計,借用進程的內存空間來儲存上下文
?那么這個上下文變量放哪里呢?怎么才能做到每個用例都可以共享呢?
結合之前的代碼,我們可以放在用例執的時候,沒個接口執行之前。也就是common的perform_case里
admin? CaseApiDef?里加兩字段
per_proc = models.TextField(blank=True, null=True, verbose_name='前置處理')post_proc = models.TextField(blank=True, null=True, verbose_name='后置處理')定義好了要在內聯表里展示
現在要用后置處理器取uuid的話用戶需要寫一下python腳本
#{case_ctx['uuid'] = parse(reslut['text'])['uuid']}加上后置處理并傳入case_ctx,順便用日志記錄下。這樣用戶才能去調用??case_ctx['uuid']?
我們再抄下之前python表達式校驗的代碼改一下就可以用了,之后我們再考慮提取復用的問題。
common里重新寫一個:
def eval_expression(input_, result, case_ctx): # 僅先實現后置處理 TODOif not input_:returnm = re.match(r"#\{.+\}", input_)if not m:raise Exception(f'內容格式不支持,請使用#{{}}包含[{input_}]')exp = m.group()if re.search(r"__.+__", input_): # __import__.os 等被過濾掉raise Exception(f'python表達式包含非法字符或操作')# 約定可提供的數據 resultlocal_params = {'result': result,'re': re,'parse': common.parse_json_like,'case_ctx': case_ctx,}try:# 執行evaleval(exp[2:-1], {}, local_params) # eval執行后會返回一個布爾值,[2:-1]做個切片去掉用戶輸入的#{}# 可以不要返回值 所以不用 if not eval_:except Exception as e:raise Exception(f'表達式執行失敗,請先修正后再執行用例。參考【{e}】')去運行用例,此時會報錯
斷點看下用戶輸入的我們取道了,但是沒寫入。上面代碼最后一行加上,
trace_msg = traceback.format_exc()?拋出詳細信息再看看
?排除后發現我們在執行用戶給的代碼時出錯了。。。
eval和exec
eval() 函數用來執行一個字符串表達式,并返回表達式的值。
?Python eval() 函數 | 菜鳥教程
exec 執行儲存在字符串或文件中的Python語句,相比于 eval,exec可以執行更復雜的 Python 代碼。??exec obj? ;??obj -- 要執行的表達式。
Python exec 內置語句 | 菜鳥教程
上面的代碼eval的時候就保存了,改成exec就能正常執行了。我們接到異常后又再拋出異常,感覺不夠優雅,干脆直接報錯,我們把異常交給調用它的函數來處理
參數傳遞
以上執行后可以發現我們?case_ctx = {}里uuid值了,那用戶要怎么用?我們想要這些字段都能用上
在common里再寫個方法,通過getattr循環取?redis_key ,?auth_username……的值,沒取到就繼續跑,取到了就給他應該默認的方法,方法暫時沒想到
?Python getattr() 函數 | 菜鳥教程
Python setattr() 函數 | 菜鳥教程
def proc_apidef_params(item: CaseApiDef, case_ctx):for attr in ['redis_key', 'auth_username', 'auth_password', 'bearer_token']:val = getattr(item, attr)if not val:continuesetattr(item, attr, '??')接下來寫??位置的函數:
把用戶輸入的val校驗里面包含#{}的排除掉,如果排除后里面是0就直接返回它本身并結束。
local_params是給用戶調用的
re.sub()是把? #{}里的替換成?new_content ,從用戶輸入的val里找
new_content ()里matched?是前面?re.sub里r"#\{.+\}"匹配到的:<re.Match object; span=(0, 19), match="#{case_ctx['uuid']}">? 我們?讓?s=matched.group()?就可以取全部組了,值就是?#{case_ctx['uuid']}
def proc_params_expression(val, case_ctx):ms = re.findall(r"#\{.+\}", val)if len(ms) == 0:return vallocal_params = {'case_ctx': case_ctx,'re': re,'parse': common.parse_json_like}def new_content(matched):s = matched.group()if re.search(r"__.+__", s): # __import__.os 等被過濾掉raise Exception(f'python表達式包含非法字符或操作[{s}]')return eval(s[2:-1], {}, local_params)return re.sub(r"#\{.+\}", new_content, val)?以上就可以通過用戶寫的python腳本取到驗證碼了,再校驗是不是數字
之后要寫登錄接口,我們想要把登錄接口傳入case_ctx里的uuid和code,需要用戶在請求體里寫
{"username":"admin", "password":"wjxMh5UdGNAWp93UlFAvrOIGd7xUXYJdjULHVkuntNmRoFtf473yQ5RI+jV9WZ9ROWU0xShcQgZp+unqbWyjNQ==", "code":"#{case_ctx['code']}", "uuid":"#{case_ctx['uuid']}"}運行下用例,現在我們的首要目的就是去把用戶寫的腳本替換成參數
請求體中的參數替換
經過分析發現在這個階段的請求體是字符串,所以在這里處理是最合適的。
我們跳進 item.get_request_bodies()?里修改一下
def get_request_bodies(self, case_ctx=None):result = {}for p in self.request_bodies.all(): # type: CaseApiDefRequestBodyif self.api.body_type in ('raw-json', 'raw-xml', 'raw-text'):# 讓用戶能用表達式獲取上下文,如果傳入case_ctx就執行表達式;沒有就返回它本身val = u.common.proc_params_expression(p.raw_value, case_ctx) if case_ctx else p.raw_valuereturn valresult.update({p.param_name: p.param_value}) # todoreturn result在用例里運行獲取test_plt_case_apidef_requestbody?表的queryset,如果傳入了case_ctx就使用參數腳本替換處理。?沒有傳入case_ctx默認返回?原始數據
?request_body = item.get_request_bodies(case_ctx=case_ctx)?傳入case_ctx,再執行下用例。發現替換成功了,但是我們要的是數字,不是字符串。這是被測平臺返回的是“72”
導致我們轉json的時候就會有異常
?我們之前給用戶開放了 'parse': common.parse_json_like? 的方法?它可以把字符串解析成python的數據類型。只要讓用戶在后置處理時使用就可以?把“72”變成72了
回到 獲取驗證碼答案里,后置處理改下?#{case_ctx['code'] = parse(result['values'])}
重新運行用例,成功了
收尾和優化
之前我們設計的傳入case_ctx才能去調用python表達式,如果用戶不傳入只單純使用表達式呢?請求頭、請求參數也需要表達式呢?
def get_request_bodies(self, case_ctx=None):result = {}for p in self.request_bodies.all(): # type: CaseApiDefRequestBodyif self.api.body_type in ('raw-json', 'raw-xml', 'raw-text'):# 讓用戶能用表達式獲取上下文return u.common.proc_param_expression(p.raw_value, case_ctx)param_name = u.common.proc_param_expression(p.param_name, case_ctx)param_value = u.common.proc_param_expression(p.param_value, case_ctx)result.update({param_name: param_value}) # todoreturn result寫好以后試試
報錯了
?排除后猜測是這里錯誤
控制臺導入這個測試下,報錯發現 1的位置必須是字符串形式,改成 “1”?就正常了
這樣就ok了
登陸成功后我們要獲取ttoken,方便其他接口的校驗
這是就需要用戶寫一下后置處理的腳本了
#{caes_ctx['token']= parse(result['text'])['token'][7]}
caes_ctx中?加入一個token,取的值是接口執行requests返回的result.text,然后找到token鍵,取第七位之后的字符串
這樣就ok了
目前有個問題,現在perform_api只支持bearer
?最后再用json schema校驗獲取用戶列表返回的json格式正常不
{"type": "object","required": ["content"],"properties": {"content": {"type": "array","items": {"type": "object","required": ["id", "username"],"properties": {"id": {"type": "number"},"username": {"type": "string"},"roles": {"type": "array","items": {"type": "object","required": [],"properties": {"id": {"type": "number"},"name": {"type": "string"},"level": {"type": "number"},"dataScope": {"type": "string"}}}},"job": {"type": "object","required": [],"properties": {"id": {"type": "number"},"name": {"type": "string"}}},"dept": {"type": "object","required": [],"properties": {"id": {"type": "number"},"name": {"type": "string"}}},"deptId": {"type": "number"},"createTime": {"type": "number"}}}},"totalElements": {"type": "number"}} }?
最后看下這個問題,現在是程序可以調用。但是pycharm不能識別過去
?只需要在utils包的ini.py里加上? ?__all__ = ['common', 'http', 'redis_', 'resp']?
需要注意的是,以后包里有其他模塊以后也要寫進去,不然以后?from?utils?import * 的時候就沒有新模塊。一般不建議?import *
總結
以上是生活随笔為你收集整理的阶段三-02 用例之间上下文传递的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: APP CPU测试
- 下一篇: 中国首个超250米高“空中连廊”幕墙工程