🌊断言扩展
内置断言
aomaker
的所有测试类,都继承于aomaker
提供的BaseTestcase
类,这个测试类内置了一些常见的断言方式:
class BaseTestcase:
@staticmethod
def assert_eq(actual_value, expected_value):
"""
equals
"""
try:
assert actual_value == expected_value
except AssertionError as e:
logger.error(f"eq断言失败,预期结果:{expected_value},实际结果:{actual_value}")
raise e
@staticmethod
def assert_gt(actual_value, expected_value):
"""
greater than
"""
try:
assert actual_value > expected_value
except AssertionError as e:
logger.error(f"gt断言失败,预期结果:{expected_value},实际结果:{actual_value}")
raise e
@staticmethod
def assert_lt(actual_value, expected_value):
"""
less than
"""
try:
assert actual_value < expected_value
except AssertionError as e:
logger.error(f"lt断言失败,预期结果:{expected_value},实际结果:{actual_value}")
raise e
@staticmethod
def assert_neq(actual_value, expected_value):
"""
not equals
"""
try:
assert actual_value != expected_value
except AssertionError as e:
logger.error(f"neq断言失败,预期结果:{expected_value},实际结果:{actual_value}")
raise e
@staticmethod
def assert_ge(actual_value, expected_value):
"""
greater than or equals
"""
try:
assert actual_value >= expected_value
except AssertionError as e:
logger.error(f"ge断言失败,预期结果:{expected_value},实际结果:{actual_value}")
raise e
@staticmethod
def assert_le(actual_value, expected_value):
"""
less than or equals
"""
try:
assert actual_value <= expected_value
except AssertionError as e:
logger.error(f"le断言失败,预期结果:{expected_value},实际结果:{actual_value}")
raise e
@staticmethod
def assert_contains(actual_value, expected_value):
assert isinstance(
expected_value, (list, tuple, dict, str, bytes)
), "expect_value should be list/tuple/dict/str/bytes type"
try:
assert expected_value in actual_value
except AssertionError as e:
logger.error(f"contains断言失败,预期结果:{expected_value},实际结果:{actual_value}")
raise e
jsonschema介绍
此外,在2.0中还加入了jsonschema
断言。
有时,我们的接口响应数据体积会非常庞大,字段也非常多,我们常规的断言可能只会去关注某几个关键字段,但是这不够健壮,有时候后端“悄悄咪咪”加了某个字段或者删了某个字段或者改了某个字段的类型,我们可能很难察觉到,这就会导致一些隐藏的bug逃逸,所以我们需要加强断言的健壮度,要对整个响应内容结构有一个基本的把控。
怎么做呢,这就需要用到jsonschema
了。
JSON Schema是基于JSON格式,用于定义JSON数据结构以及校验JSON数据内容。 JSON Schema官网地址:http://json-schema.org/
比如有一个json字符串:
{
"name": "aomaker",
"age": 2,
"desc": "api framework"
}
这其中name
和age
是必填字段,字段类型分别是string
和int
,可选字段是desc
,字段类型是string
,假如我想要每个这样的json字符串,都符合上面的约束,那我怎么自动去校验呢?这就需要安装jsonschema
的语法去写约束条件。
jsonchema语法
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "TestInfo",
"description": "some information about test",
"type": "object",
"properties": {
"name": {
"description": "Name of the test",
"type": "string"
},
"age": {
"description": "age of test",
"type": "integer"
}
},
"required": [
"name",
"age"
]
}
开始校验
from jsonschema import validate
x = {
"name": "aomaker",
"age": 2,
"desc": "api framework"
}
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "TestInfo",
"description": "some information about test",
"type": "object",
"properties": {
"name": {
"description": "Name of the test",
"type": "string"
},
"age": {
"description": "age of test",
"type": "integer"
}
},
"required": [
"name"
]
}
validate(x, schema)
如果校验通过,会没有返回,也没有报错。
假如不小心把age
传成了字符串"2"
,jsonschema
检测到后,会立即报错
jsonschema.exceptions.ValidationError: '2' is not of type 'integer'
Failed validating 'type' in schema['properties']['age']:
{'description': 'age of test', 'type': 'integer'}
On instance['age']:
'2'
假如不小心没有传必填字段name
,也会立即报错
jsonschema.exceptions.ValidationError: 'name' is a required property
Failed validating 'required' in schema:
{'$schema': 'http://json-schema.org/draft-04/schema#',
'description': 'some information about test',
'properties': {'age': {'description': 'age of test',
'type': 'integer'},
'name': {'description': 'Name of the test',
'type': 'string'}},
'required': ['name', 'age'],
'title': 'TestInfo',
'type': 'object'}
On instance:
{'age': 2, 'desc': 'api framework'}
通过这种手段是不是对我们测试过的接口更有信心了?
扩展jsonschema断言
但是~有没有发现,jsonschema
虽然很好,但是你得手动去写jsonschema
校验语法,上面的示例还好,只有几个字段,但实际业务中,响应的字段可能要比这个多得多得多吧?那每个接口这么去写,成本也太高了!
所以, aomaker
提供了一种手段,可以自动去生成jsonschema
校验语法~其实上文有提到过,aomaker.db
数据库中,有一张表叫schema
,其实它就是存放请求过的每个接口响应的jsonschema
校验语法的。aomaker
会自动记录每个请求第一次返回响应时的jsonschema
,当在case层去做断言时,aomaker
提供了一个jsonschema
的断言在BaseTestcase
中:
class BaseTestcase:
...
@staticmethod
def assert_schema(instance, api_name):
"""
Assert JSON Schema
:param instance: 请求响应结果
:param api_name: 存放在schema表中的对应key名
:return:
"""
json_schema = Schema().get(api_name)
if json_schema is None:
logger.error('jsonschema未找到!')
raise SchemaNotFound(api_name)
try:
validate(instance, schema=json_schema)
except ValidationError as msg:
logger.error(msg)
raise AssertionError
在case层进行断言:
class TestJob(BaseTestcase):
def test_hpc_submit_job(self):
resp = job.submit_hpc_job()
ret_code = resp.get("ret_code")
self.assert_eq(ret_code, 0)
# schema断言
self.assert_schema(resp, 'submit_hpc_job')
会根据第二个参数,去schema
表中取对应key的jsonschema
schema表
需要注意的是,schema
表中的jsonschema
是在接口被调用的时候自动存储的,所以不需要手动操作,但是也是因为这个原因,有可能会存储接口异常时的响应的结构体,所以当要做jsonschema断言时,请检查该表,确保该表对应接口的jsonschema是符合预期的,当然,aomaker也提供了一个genson()
方法,你可以通过该方法手动获取预期的jsonschema
,然后自己存储到schema
表中,schema
表是固化不会自动清理的,你可以持续校正和维护该表。
如何使用genson
只需要传入json字符串,genson
会自动返回其jsonschema
。
# 导入genson
from aomaker.aomaker import genson
x = {
"age": 2,
"desc": "api framework"
}
schema = genson(x)
print(schema)
# 输出:
# {'$schema': 'http://json-schema.org/schema#', 'type': 'object', 'properties': {'age': {'type': 'integer'}, 'desc': {'type': 'string'}}, 'required': ['age', 'desc']}