在接口测试中,如果每个字段都通过 assert 单独校验,不仅代码冗长,而且维护成本较高。JSON Schema 提供了一种标准化的数据校验方案,可以帮助我们快速验证接口返回结果是否符合预期。

一、什么是 JSON Schema?

JSON Schema 是一种用于描述和校验 JSON 数据结构的规范,可以理解为 JSON 数据的“规则说明书”。

例如你去水果店购买水果捞,老板规定:

  • 必须包含西瓜和葡萄;
  • 水果种类不能少于 3 种;
  • 每种水果都必须填写名称和重量;

这些规则就相当于一个 Schema。

而水果捞本身则相当于 JSON 数据。

老板会根据这份规则检查你的水果捞是否符合要求,而 JSON Schema 的作用就是检查 JSON 数据是否符合预先定义好的规则。

二、基础校验:type 和 required

2.1 最简单的校验

from jsonschema import validate

json_schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"}
    }
}

# 即使缺少 age,校验也会通过(因为没有 required)
validate(instance={"name": "张三"}, schema=json_schema)

关键点: 没有 required 约束时,缺字段不会报错。

2.2 必填字段:required

json_schema = {
    "type": "object",
    "required": ["code", "data"],
    "properties": {
        "code": {"type": "string"},
        "errMsg": {"type": "string"},
        "data": {"type": "array"}
    }
}

# code 和 data 必须存在
validate(instance={"code": "200", "data": []}, schema=json_schema)

required 是一个数组,列出所有必须存在的字段名。少一个就报错。

三、数组校验

3.1 数组元素类型

json_schema = {
    "type": "object",
    "properties": {
        "data": {
            "type": "array",
            "items": {"type": "integer"}
        }
    }
}

validate(instance={"data": [1, 2, 3]}, schema=json_schema)

3.2 数组长度与唯一性

json_schema = {
    "type": "object",
    "properties": {
        "data": {
            "type": "array",
            "items": {"type": "integer"},
            "minItems": 5,      # 最少 5 个
            "maxItems": 10,     # 最多 10 个
            "uniqueItems": True # 元素必须唯一
        }
    }
}

# 通过:data = [1, 2, 3, 4, 5]
# 不通过:data = [1, 2, 3, 3, 5](3 重复了)

四、数组元素为对象

实际开发中,数组元素往往是对象,这时就要嵌套定义:

json_schema = {
    "type": "object",
    "required": ["code", "data"],
    "properties": {
        "code": {"type": "string"},
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": ["id", "title", "author"],
                "properties": {
                    "id": {"type": "integer"},
                    "title": {"type": "string"},
                    "author": {"type": "string"},
                    "content": {"type": "string"}
                }
            }
        }
    }
}

# 通过:[{"id": 1, "title": "博客1", "author": "张三"}]
# 不通过:[{"id": 1, "title": "博客1"}](缺少 author)

注意: required 可以出现在每一层,顶层对象有顶层的必填字段,嵌套对象有嵌套的必填字段。

五、数值约束

json_schema = {
    "type": "object",
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "exclusiveMinimum": 0,  # id > 0
                        "exclusiveMaximum": 4   # id < 4
                    },
                    "age": {
                        "type": "integer",
                        "minimum": 10,   # age >= 10
                        "maximum": 60    # age <= 60
                    }
                }
            }
        }
    }
}
关键字 含义 示例
minimum >= minimum: 10 表示 ≥ 10
maximum <= maximum: 60 表示 ≤ 60
exclusiveMinimum > 表示大于,不能等于
exclusiveMaximum < 表示小于,不能等于

记忆口诀: 前面加 exclusive 就是开区间(> 或 <),不加就是闭区间(>= 或 <=)。

六、字符串约束

json_schema = {
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "minLength": 9,   # 最少 9 个字符
            "maxLength": 10   # 最多 10 个字符
        },
        "gender": {
            "type": "string",
            "enum": ["male", "female"]  # 只能在这几个值中选
        }
    }
}

enum 限定字段的可选值,非常适合性别、状态码等有限选项的场景。

七、对象属性数量约束

7.1 minProperties / maxProperties

控制对象有多少个属性字段:

json_schema = {
    "type": "object",
    "minProperties": 2,   # 至少 2 个字段
    "maxProperties": 3,   # 最多 3 个字段
    "properties": {
        "code": {"type": "string"},
        "errMsg": {"type": "string"},
        "data": {"type": "array"}
    }
}

# 传 {code, errMsg, data} 3 个字段 → 通过
# 传 {code, errMsg, data, extra} 4 个字段 → 不通过

7.2 additionalProperties

控制是否允许 properties 中未定义的字段:

json_schema = {
    "type": "object",
    "properties": {
        "id": {"type": "integer"},
        "title": {"type": "string"}
    },
    "additionalProperties": True  # 允许额外字段
    # 如果设为 False,出现其他字段就会报错
}

八、依赖关系:dependentRequired

当一个字段出现时,要求另一个字段也必须出现:

json_schema = {
    "type": "object",
    "properties": {
        "username": {"type": "string"},
        "age": {"type": "integer", "minimum": 18, "maximum": 100},
        "height": {"type": "integer", "minimum": 150, "maximum": 200},
        "gender": {"type": "string", "enum": ["male", "female"]}
    },
    "dependentRequired": {
        "age": ["height", "gender"]
    }
}

# 传了 age,height 和 gender 也必须传
# 不传 age,height 和 gender 可以不存在

九、综合实战:博客接口响应校验

下面是一个完整的接口响应校验示例,结合了今天学的大部分知识点:

import requests
from jsonschema import validate

url = "http://47.97.164.187:9090/blog/getList"
header = {"user_token_header": "eyJhbGciOiJIUzI1NiJ9..."}

r = requests.get(url, headers=header)

json_schema = {
    "type": "object",
    "required": ["code", "errMsg", "data"],
    "minProperties": 2,
    "maxProperties": 3,
    "properties": {
        "code": {"type": "string"},
        "errMsg": {"type": "string"},
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "id", "title", "content", "userId",
                    "deleteFlag", "createTime", "updateTime", "loginUser"
                ],
                "minProperties": 7,
                "maxProperties": 9,
                "additionalProperties": True,
                "properties": {
                    "id": {"type": "integer", "exclusiveMinimum": 0},
                    "title": {"type": "string", "minLength": 1},
                    "content": {"type": "string"},
                    "userId": {"type": "integer", "minimum": 1},
                    "deleteFlag": {"type": "integer", "enum": [0, 1]},
                    "createTime": {"type": "string"},
                    "updateTime": {"type": "string"},
                    "loginUser": {"type": "boolean"}
                }
            }
        }
    }
}

validate(r.json(), json_schema)
print("接口数据校验通过!")

十、总结

知识点 关键字 用途
类型 type 指定字段类型(string, integer, number, boolean, array, object)
必填 required 指定哪些字段必须存在
字符串长度 minLength / maxLength 控制字符串长度
数值范围 minimum / maximum 控制数值范围(闭区间)
数值范围 exclusiveMinimum / exclusiveMaximum 控制数值范围(开区间)
数组长度 minItems / maxItems 控制数组元素个数
数组唯一 uniqueItems 数组元素是否必须唯一
属性数量 minProperties / maxProperties 控制对象字段个数
额外属性 additionalProperties 是否允许未定义的字段
枚举 enum 限定可取值列表
依赖 dependentRequired 字段出现时的连带必填

JSON Schema 就像是给 JSON 数据上的一份"体检单"——通过它,你可以精确地把控接口数据的质量,在自动化测试中尤为实用。写接口测试时,用 Schema 校验代替单纯的字段存在性检查,能让你的测试更严谨、更专业!


十一、问答练习

11.1 type 类型校验

题目: 以下 Schema 中,传 {"age": 20.5} 能通过校验吗?

{"type": "object", "properties": {"age": {"type": "integer"}}}

不能通过。 20.5 是浮点数类型,但 Schema 要求 integer(整数)。在 JSON Schema 中 integer只能是整数 number既可以是整数也可以是浮点数。


11.2 required 必填字段

题目:{}(空对象)能通过吗?

{"type": "object", "required": ["name"], "properties": {"name": {"type": "string"}}}

不能通过。 required 要求 name 字段必须存在,所以最少都要传name字段才可以通过。


11.3 minLength / maxLength 字符串长度

题目: "name": "hello" 能通过吗?

"name": {"type": "string", "minLength": 5, "maxLength": 10}

能通过。 "hello" 长度为 5,满足 minLength: 5(≥5)且 maxLength: 10(≤10)。


11.4 minimum / maximum 数值范围

题目: age: 18 能通过吗?age: 60 呢?

"age": {"type": "integer", "minimum": 18, "maximum": 60}

都能通过。 minimum: 18 表示 ≥18,maximum: 60 表示 ≤60,18 和 60 都在闭区间 [18, 60] 内,临界值18与60可以通过校验。


11.5 exclusiveMinimum / exclusiveMaximum 数值范围

题目: id: 0id: 1 哪个能通过?

"id": {"type": "integer", "exclusiveMinimum": 0}

id: 1 能通过id: 0 不能。 exclusiveMinimum: 0 表示 id > 0,因此 0 无法通过校验,而 1 可以通过。exclusiveMinimum 和 exclusiveMaximum 表示不包含边界值,即临界值本身无法通过校验。


11.6 minItems / maxItems 数组长度

题目: data: [1, 2] 能通过吗?

"data": {"type": "array", "items": {"type": "integer"}, "minItems": 3, "maxItems": 5}

不能通过。 minItems: 3 要求数组至少 3 个元素,实际data中只有 2 个。


11.7 uniqueItems 数组唯一性

题目: data: [1, 2, 3, 2] 能通过吗?

"data": {"type": "array", "items": {"type": "integer"}, "uniqueItems": True}

不能通过。 uniqueItems: True 要求数组元素全部唯一,但 2 出现了两次。


11.8 minProperties / maxProperties 对象属性数量

题目:{"a": 1, "b": 2, "c": 3, "d": 4} 能通过吗?

{"type": "object", "minProperties": 2, "maxProperties": 3}

不能通过。 规定了字段数量在[2,3]之间,现在传过来的有 4 个字段,超过了 maxProperties: 3 的限制。


11.9 additionalProperties 额外属性

题目:{"id": 1, "title": "A", "extra": "x"} 能通过吗?

{
  "type": "object",
  "properties": {"id": {"type": "integer"}, "title": {"type": "string"}},
  "additionalProperties": False
}

不能通过。 additionalProperties: False 禁止出现 properties 中未定义的字段,extra 不在定义中。


11.10 enum 枚举

题目: gender: "other" 能通过吗?

"gender": {"type": "string", "enum": ["male", "female"]}

不能通过。 enum 限定了只能取 "male""female""other" 不在列表中。


11.11 dependentRequired 依赖关系

题目:{"age": 20} 能通过吗?

{
  "type": "object",
  "properties": {"age": {"type": "integer"}, "height": {"type": "integer"}},
  "dependentRequired": {"age": ["height"]}
}

不能通过。 dependentRequired 规定:如果传了 age,则 height 也必须传。这里传了 age 但缺少 height。但是反过来传了"height"字段,如果没有其他特殊设置"age"不传过来也是可以的


11.12 综合实战题

题目: 以下 JSON 能通过校验吗?指出所有问题。

json = {
    "code": "200",
    "data": [
        {"id": 0, "title": "a"},
        {"id": 2, "title": "bb", "author": "李四"}
    ]
}

schema = {
    "type": "object",
    "required": ["code", "data"],
    "properties": {
        "code": {"type": "string"},
        "data": {
            "type": "array",
            "minItems": 1,
            "items": {
                "type": "object",
                "required": ["id", "title", "author"],
                "properties": {
                    "id": {"type": "integer", "exclusiveMinimum": 0},
                    "title": {"type": "string", "minLength": 2},
                    "author": {"type": "string"}
                }
            }
        }
    }
}

不能通过,第一个元素存在三个问题:

  1. id: 0 — 不满足 exclusiveMinimum: 0(需要 id > 0)
  2. title: "a" — 长度为 1,不满足 minLength: 2
  3. 缺少 authorrequired 中要求了 author 字段

第二个元素 {"id": 2, "title": "bb", "author": "李四"} 校验全部通过

由于数组中有一个元素校验失败,整体校验不通过。修正建议:将第一个元素的 id 改为 ≥1,title 补足 2 个字符以上,并补上 author 字段。


以上答案仅供参考,是我个人根据学习的内容整理的,如果有不同的见解,或是发现我有错误的地方,欢迎探讨。😃

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐