前言
进行接口自动化时,有时候我们需要断言的数据比较多,一个字段一个字段进行断言比较麻烦,如果可以直接断言整个响应结果,岂不美哉,那该如何实现该功能呢?
递归
在进入正式实现前,我们先简单说一下递归。因为该功能我们主要使用递归实现,以防小伙伴们看不懂。
是什么?
递归是一种算法或函数设计方法,它通过将一个问题不断分解成规模更小的子问题来解决原始问题。在 Python 中,递归是一种函数自身调用自身的过程。通过递归,程序可以在问题空间中向下深入,并通过返回值将解决子问题的结果合并起来,最终获得整个问题的解。
如何用?
在 Python 中使用递归,首先需要定义边界条件,即递归的结束条件。当满足边界条件时,递归停止,返回结果。同时,还需要定义递归情况,即在未满足边界条件时,如何继续递归调用函数来解决子问题。递归函数应该能够将问题领域缩小,使其逐渐接近边界条件。
隐藏风险
首先,递归可能会导致堆栈溢出,特别是在处理大规模问题时。其次,递归的执行效率可能较低,因为每次递归都需要保存函数调用的上下文。此外,递归需要合理设置边界条件,否则可能导致无限循环和程序崩溃。
在编写递归函数时,需要仔细考虑边界条件和递归情况,确保递归能够正确结束,并得到期望的结果。同时,为了提高递归性能,可以考虑使用尾递归优化或迭代等技术。
案例实现
有了递归的基本了解,接下来进入正题,我们将实现如何断言整个接口响应数据。
class AssertInfo:
data = []
def diff_json(response_data, assert_data):
if isinstance(response_data, dict):
for key in assert_data:
if key not in response_data:
info = f"Response data has no key: {key}"
print(info)
AssertInfo.data.append(info)
for key in response_data:
if key in assert_data:
diff_json(response_data[key], assert_data[key])
else:
info = f"Assert data has not key: {key}"
print(info)
elif isinstance(response_data, list):
if len(response_data) == 0:
print("response is []")
if len(response_data) != len(assert_data):
print(f"list len: '{len(response_data)}' != '{len(assert_data)}'")
if response_data:
if isinstance(response_data[0], dict):
response_data = sorted(response_data, key=lambda x: x[list(response_data[0].keys())[0]])
else:
response_data = sorted(response_data)
if assert_data:
if isinstance(assert_data[0], dict):
assert_data = sorted(assert_data, key=lambda x: x[list(assert_data[0].keys())[0]])
else:
assert_data = sorted(assert_data)
for src_list, dst_list in zip(response_data, assert_data):
diff_json(src_list, dst_list)
else:
if str(response_data) != str(assert_data):
info = f"Value are not equal: {response_data}"
print(info)
AssertInfo.data.append(info)
代码其实也不难理解,我们做一个简单解释:
这是一个用于比较两个 JSON 数据格式是否相同的函数。每个部分的功能:
函数名称:diff_json(response_data, assert_data)
- 参数:
response_data
是接口响应数据,assert_data
是期望的断言数据。
if isinstance(response_data, dict):
-
如果
response_data
是字典类型,则进入该条件判断。 -
循环遍历
assert_data
中的每个键(key):- 如果键(key)不在
response_data
中,则打印信息表示响应数据缺少该键(key)。
- 如果键(key)不在
-
循环遍历
response_data
中的每个键(key):- 如果键(key)在
assert_data
中,则递归调用diff_json
函数进行比较。 - 否则,打印信息表示断言数据缺少该键(key)。
- 如果键(key)在
elif isinstance(response_data, list):
-
如果
response_data
是列表类型,则进入该条件判断。 -
检查响应数据和断言数据的长度是否相等,如果不相等,则打印信息表示长度不一致。
-
如果
response_data
不为空:- 如果列表中的元素是字典类型,按照字典键(key)的值进行排序。
- 如果列表中的元素不是字典类型,进行普通的排序。
-
如果
assert_data
不为空:- 如果列表中的元素是字典类型,按照字典键(key)的值进行排序。
- 如果列表中的元素不是字典类型,进行普通的排序。
-
使用
zip
函数同时迭代response_data
和assert_data
:- 对于每个对应位置的元素,递归调用
diff_json
函数进行比较。
- 对于每个对应位置的元素,递归调用
else:
- 如果
response_data
既不是字典类型也不是列表类型,则进入该条件判断。 - 如果
response_data
和assert_data
的值不相等,则打印信息表示值不相等。
这个函数通过递归的方式,遍历并比较两个 JSON 数据结构的每一个键(key)和值。如果存在差异,将会打印出对应的信息。在需要断言和验证接口返回数据时,可以使用该函数进行检查。
好了,那我们看一看测试效果:
response_data = {
"name": "Alice",
"age": 25,
"email": "alice@example.com"
}
assert_data = {
"name": "Alice",
"email": "Alice@example.com"
}
diff_json(response_data, assert_data)
执行之后,会打印出如下结果:
Assert data has not key: age
Value are not equal: alice@example.com
完美解决!
最后
这个功能还是很实用的,大部分接口响应内容都比较复杂,想要断言的内容比较多时就比较麻烦,而这个函数可以比较两个 JSON 数据格式是否相同,节省了很多时间,提高编写接口用例的效率。