🌊断言扩展

内置断言

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"
}

这其中nameage是必填字段,字段类型分别是stringint,可选字段是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表

jROZuT.md.png

需要注意的是,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']}

results matching ""

    No results matching ""