断言是一种除错机制,用于验证代码是否符合编码人员的预期。
pytest自带的assert断言有以下几种语法:
import time
# 测试类名必须以Test开头,并且不能有init方法class Test1: # • assert xx :判断xx为真 # • assert not xx :判断xx不为真 # • assert a in b :判断b包含a # • assert a == b :判断a 等于b # • assert a != b :判断a不等于b
#测试方法必须以test开头 def test_01(self): print('------------方法test_01------------') assert "a" in "abc"
def test_02(self): print('------------方法test_02------------') assert 3+2 == 5
正确会自动跳过

错误会有以下提示

这样我们可以忽略正确的用例,只关注错误的用例,提高每次自动化的整体效率。
为了适配不同的场景,我这边写了一个方法,包含相同匹配和包含匹配:
qw_result为一个数组,数组里一个对象为一个期望结果。对象的key只能写contains或者equals,value写期望的内容。
import time
# 测试类名必须以Test开头,并且不能有init方法import jsonpath
class Test1: # • assert xx :判断xx为真 # • assert not xx :判断xx不为真 # • assert a in b :判断b包含a # • assert a == b :判断a 等于b # • assert a != b :判断a不等于b
#测试方法必须以test开头 def test_01(self): print('------------方法test_01------------') qw_result = [ {"contains":"you are right"}, {"equals": {"time": 20201011} }, {"equals": {"status_code": 200} } ]
sj_result = {"result_task":"you are right", "time":20201011} si_return_code = 200
self.assert_result( qw_result, sj_result, si_return_code)
def test_02(self): print('------------方法test_02------------')
# 断言 def assert_result(self, qw_result, sj_result, return_code): all_flag = 0 for yq in qw_result: for key, value in yq.items(): # print(key,value) if key == "equals": flag = self.equals_assert(value, sj_result, return_code) all_flag = all_flag + flag elif key == "contains": flag = self.contains_assert(value, sj_result) all_flag = all_flag + flag else: print("框架暂不支持这种断言方式")
assert all_flag == 0
# 相等断言 def equals_assert(self, value, sj_result, return_code): flag = 0 for assert_key, assert_value in value.items(): # print(assert_key,assert_value) if assert_key == "status_code": if assert_value != return_code: flag = flag + 1 print("断言失败,返回的状态码不等于%s" % return_code) else: # list为实际json匹配出来的值 lists = jsonpath.jsonpath(sj_result, '$..%s' % assert_key) if lists: if assert_value not in lists: flag = flag + 1 print(f"断言失败:{assert_key}不等于{assert_value}") else: flag = flag + 1 print(f"断言失败:返回的结果不存在{assert_key}") return flag
# 包含断言 def contains_assert(self, value, sj_result): flag = 0 if value not in str(sj_result): flag = flag + 1 print(f"断言失败,返回结果中不包含{value}")
return flag
如果不匹配,输出结果会有提示:

最后将该方法写到之前的request_util里:
import jsonimport reimport requestsimport jsonpath
from test7.commons.debug_util import DebugTalkfrom test7.commons.yaml_util import write_extract_yaml
class RequestUtil: session = requests.session()
# 替换值的方法 def replace_value(self, data): # 1保存数据类型 data_type = type(data)
# 2判断数据类型 if isinstance(data, dict) or isinstance(data, list): str_data = json.dumps(data) else: str_data = str(data) # 替换 for cs in range(1, str_data.count('${') + 1): if "${" in str_data and "}" in str_data: start_index = str_data.index("${") end_index = str_data.index("}", start_index) old_value = str_data[start_index:end_index + 1] # print(f"old_value:{old_value}")
# 反射:通过类的对象和方法字符串去调用方法 function_name = old_value[2:old_value.index('(')] args_value1 = old_value[old_value.index('(') + 1:old_value.index(')')] if args_value1 != "": args_value2 = args_value1.split(',') # print(function_name, args_value2) new_value = getattr(DebugTalk(), function_name)(*args_value2) else: new_value = getattr(DebugTalk(), function_name)()
# print(f"new_value:{new_value}") # 判断替换的新参数的类型 if isinstance(new_value, int) or isinstance(new_value, float): str_data = str_data.replace('"' + old_value + '"', str(new_value)) else: str_data = str_data.replace(old_value, str(new_value))
# 3还原数据类型 if isinstance(data, dict) or isinstance(data, list): data = json.loads(str_data) else: data = data_type(str_data) return data
#规范YAML测试用例 def standard_yaml(self,caseinfo): caseinfo_keys = caseinfo.keys() # 判断关键词是否完整 if "name" in caseinfo_keys and "request" in caseinfo_keys and "validate" in caseinfo_keys: cs_request = caseinfo['request'] cs_request_keys = cs_request.keys() if "method" in cs_request_keys and "url" in cs_request_keys: method = cs_request.pop("method") #pop-删除列表里最后一个并且返回这个值 url = cs_request.pop("url") res = self.send_request(method,url,**cs_request) print(res.text) # 写入yaml文件 return_json = "" return_code = res.status_code try: return_json = res.json() except Exception as e: print("返回的结果不是json格式,不能使用jsonpath提取")
# 提取值并写入extract.yaml文件 if "extract" in caseinfo_keys: for key, value in caseinfo["extract"].items(): if "(.*?)" in value or "(.+?)" in value: zz_value = re.search(value, res.text) if zz_value: extract_value = {key: zz_value.group(1)} write_extract_yaml(extract_value) else: json_value = jsonpath.jsonpath(return_json, value) if json_value: extract_value = {key: json_value[0]} write_extract_yaml(extract_value) # 断言 if caseinfo['validate'] != "none": yq_resulte = caseinfo['validate'] sj_resulte = return_json self.assert_result(yq_resulte, sj_resulte, return_code) # 返回response对象 return res else: print("二级关键字必须包含:method,url") else: print("一级关键字必须包含:name,request,validate")
#统一请求封装 def send_request(self, method, url, **kwargs): method = str(method).lower() url = self.replace_value(url)
# 请求头和参数替换 for key, value in kwargs.items(): if key in ['params', 'data', 'json', 'headers']: kwargs[key] = self.replace_value(value) print(f"kwargs[key]:{kwargs[key]}") elif key == "files": for file_key, file_path in value.items(): value[file_key] = open(file_path, 'rb')
res = RequestUtil.session.request(method,url,**kwargs) return res
# 断言 def assert_result(self, yq_result, sj_result, return_code): all_flag = 0 for yq in yq_result: for key, value in yq.items(): # print(key,value) if key == "equals": flag = self.equals_assert(value, sj_result, return_code) all_flag = all_flag + flag elif key == "contains": flag = self.contains_assert(value, sj_result) all_flag = all_flag + flag else: print("框架暂不支持这种断言方式")
assert all_flag == 0
# 相等断言 def equals_assert(self, value, sj_result, return_code): flag = 0 for assert_key, assert_value in value.items(): # print(assert_key,assert_value) if assert_key == "status_code": if assert_value != return_code: flag = flag + 1 print("断言失败,返回的状态码不等于%s" % return_code) else: # list为实际json匹配出来的值 lists = jsonpath.jsonpath(sj_result, '$..%s' % assert_key) if lists: if assert_value not in lists: flag = flag + 1 print(f"断言失败:{assert_key}不等于{assert_value}") else: flag = flag + 1 print(f"断言失败:返回的结果不存在{assert_key}") return flag
# 包含断言 def contains_assert(self, value, sj_result): flag = 0 if value not in str(sj_result): flag = flag + 1 print(f"断言失败,返回结果中不包含{value}")
return flag
修改pm_get_token.yaml,把要校验的validate内容填进去:
- name: 获取token鉴权码 request: method: get url: https://api.weixin.qq.com/cgi-bin/token params: grant_type: client_credential appid: wx8e8b67ced3c4b884 secret: 27c524bd9ca932e31e229be30b0a805b validate: - equals: {status_code: 200} - equals: {expires_in: 7200} - contains: access_token extract: access_token: '"access_token":"(.*?)"' expires_in: $.expires_in
测试一下,断言成功生效:

断言失败生效:

接口测试重要的断言也就做好了。
最近太热了,发布完就去吃雪糕。
最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走

这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助…….【下方】