参考官网: https://python-jsonschema.readthedocs.io/en/stable/

应用场景

在做接口测试的时候,服务器返回的结果中的数据比较多,我们在进行数据验证的时候,可以对数据的主要自动进行验证。主要字段验证,使用之前的方式都可以实现(使用对应的Python基本类型中相关方法即可实现)

如果要对整个数据的类型进行验证,比如 服务器返回的结果

  1. {"name" : "Eggs", "price" : 34.99}

name 字段是确定的, name的值为 字符串。但是name具体值不确定,
price 字段也是确定的, price的值为数字,但是具体值也不确定。

在接口测试过程中,要对具体的确定的部分进行必要断言。 使用 schema 方式进行断言。
可以声明 数据的类型

  1. schema = {
  2. "type" : "object",
  3. "properties" : {
  4. "price" : {"type" : "number"},
  5. "name" : {"type" : "string"},
  6. },
  7. }

通过声明类型的方式 对值进行断言。

安装包

  1. pip install jsonschema

快速开始

将数据的类型定义为 schema 使用 jsonschema 进行验证。
main.py 文件

  1. # 导入jsonschema
  2. from jsonschema import validate
  3. # 定义 schema
  4. schema = {
  5. "type" : "object", # 类型为对象类型
  6. "properties" : { # 主要字段的属性
  7. "price" : {"type" : "number"}, # price 字段类型为 数字
  8. "name" : {"type" : "string"}, # name 字段的类型为 字符串
  9. },
  10. "required":["name","price"] # name, price 两个字段必须存在
  11. }
  12. def test_01():
  13. testdata = {
  14. "name":"apple",
  15. "price":20
  16. }
  17. validate(instance=testdata,schema=schema)
  18. def test_02():
  19. """
  20. 执行失败
  21. :return:
  22. """
  23. testdata = {
  24. "name":"banana",
  25. "price":None # None 不是数字类型
  26. }
  27. validate(instance=testdata,schema=schema)
  28. def test_03():
  29. """
  30. 会报错, 字段 names 不存在
  31. :return:
  32. """
  33. testdata = {
  34. "names":"apple",
  35. "price":20.1
  36. }
  37. validate(instance=testdata,schema=schema)

使用 pytest 执行

  1. pytest -s -v main.py

image.png

实际生产环境中

上面例子中的数据比较简单,在公司中的项目一个接口返回的数据,数据量可能会比较大,如何设置schema?
推荐使用一些在线工具生成对应的schame.
https://www.jsonschema.net/home (这个需要翻墙)
使用 https://tooltt.com/json2schema/ (这个可以直接使用)

在网站上设置必要的字段
image.png
将返回结果复制到网站中,可以自动生成对应得schema。
image.png

将对应的schema信息保存到文件中
schema.json

  1. {
  2. "$schema": "http://json-schema.org/draft-07/schema",
  3. "$id": "http://example.com/example.json",
  4. "type": "object",
  5. "required": [
  6. "resultCode",
  7. "message",
  8. "data"
  9. ],
  10. "properties": {
  11. "resultCode": {
  12. "$id": "#/properties/resultCode",
  13. "type": "integer"
  14. },
  15. "message": {
  16. "$id": "#/properties/message",
  17. "type": "string"
  18. },
  19. "data": {
  20. "$id": "#/properties/data",
  21. "type": "object",
  22. "required": [
  23. "carousels",
  24. "hotGoodses",
  25. "newGoodses",
  26. "recommendGoodses"
  27. ],
  28. "properties": {
  29. "carousels": {
  30. "$id": "#/properties/data/properties/carousels",
  31. "type": "array",
  32. "items": {
  33. "$id": "#/properties/data/properties/carousels/items",
  34. "anyOf": [
  35. {
  36. "$id": "#/properties/data/properties/carousels/items/anyOf/0",
  37. "type": "object",
  38. "required": [
  39. "carouselUrl",
  40. "redirectUrl"
  41. ],
  42. "properties": {
  43. "carouselUrl": {
  44. "$id": "#/properties/data/properties/carousels/items/anyOf/0/properties/carouselUrl",
  45. "type": "string"
  46. },
  47. "redirectUrl": {
  48. "$id": "#/properties/data/properties/carousels/items/anyOf/0/properties/redirectUrl",
  49. "type": "string"
  50. }
  51. }
  52. }
  53. ]
  54. }
  55. },
  56. "hotGoodses": {
  57. "$id": "#/properties/data/properties/hotGoodses",
  58. "type": "array",
  59. "items": {
  60. "$id": "#/properties/data/properties/hotGoodses/items",
  61. "anyOf": [
  62. {
  63. "$id": "#/properties/data/properties/hotGoodses/items/anyOf/0",
  64. "type": "object",
  65. "required": [
  66. "goodsId",
  67. "goodsName",
  68. "goodsIntro",
  69. "goodsCoverImg",
  70. "sellingPrice",
  71. "tag"
  72. ],
  73. "properties": {
  74. "goodsId": {
  75. "$id": "#/properties/data/properties/hotGoodses/items/anyOf/0/properties/goodsId",
  76. "type": "integer"
  77. },
  78. "goodsName": {
  79. "$id": "#/properties/data/properties/hotGoodses/items/anyOf/0/properties/goodsName",
  80. "type": "string"
  81. },
  82. "goodsIntro": {
  83. "$id": "#/properties/data/properties/hotGoodses/items/anyOf/0/properties/goodsIntro",
  84. "type": "string"
  85. },
  86. "goodsCoverImg": {
  87. "$id": "#/properties/data/properties/hotGoodses/items/anyOf/0/properties/goodsCoverImg",
  88. "type": "string"
  89. },
  90. "sellingPrice": {
  91. "$id": "#/properties/data/properties/hotGoodses/items/anyOf/0/properties/sellingPrice",
  92. "type": "integer"
  93. },
  94. "tag": {
  95. "$id": "#/properties/data/properties/hotGoodses/items/anyOf/0/properties/tag",
  96. "type": "string"
  97. }
  98. }
  99. }
  100. ]
  101. }
  102. },
  103. "newGoodses": {
  104. "$id": "#/properties/data/properties/newGoodses",
  105. "type": "array",
  106. "items": {
  107. "$id": "#/properties/data/properties/newGoodses/items",
  108. "anyOf": [
  109. {
  110. "$id": "#/properties/data/properties/newGoodses/items/anyOf/0",
  111. "type": "object",
  112. "required": [
  113. "goodsId",
  114. "goodsName",
  115. "goodsIntro",
  116. "goodsCoverImg",
  117. "sellingPrice",
  118. "tag"
  119. ],
  120. "properties": {
  121. "goodsId": {
  122. "$id": "#/properties/data/properties/newGoodses/items/anyOf/0/properties/goodsId",
  123. "type": "integer"
  124. },
  125. "goodsName": {
  126. "$id": "#/properties/data/properties/newGoodses/items/anyOf/0/properties/goodsName",
  127. "type": "string"
  128. },
  129. "goodsIntro": {
  130. "$id": "#/properties/data/properties/newGoodses/items/anyOf/0/properties/goodsIntro",
  131. "type": "string"
  132. },
  133. "goodsCoverImg": {
  134. "$id": "#/properties/data/properties/newGoodses/items/anyOf/0/properties/goodsCoverImg",
  135. "type": "string"
  136. },
  137. "sellingPrice": {
  138. "$id": "#/properties/data/properties/newGoodses/items/anyOf/0/properties/sellingPrice",
  139. "type": "integer"
  140. },
  141. "tag": {
  142. "$id": "#/properties/data/properties/newGoodses/items/anyOf/0/properties/tag",
  143. "type": "string"
  144. }
  145. }
  146. }
  147. ]
  148. }
  149. },
  150. "recommendGoodses": {
  151. "$id": "#/properties/data/properties/recommendGoodses",
  152. "type": "array",
  153. "items": {
  154. "$id": "#/properties/data/properties/recommendGoodses/items",
  155. "anyOf": [
  156. {
  157. "$id": "#/properties/data/properties/recommendGoodses/items/anyOf/0",
  158. "type": "object",
  159. "required": [
  160. "goodsId",
  161. "goodsName",
  162. "goodsIntro",
  163. "goodsCoverImg",
  164. "sellingPrice",
  165. "tag"
  166. ],
  167. "properties": {
  168. "goodsId": {
  169. "$id": "#/properties/data/properties/recommendGoodses/items/anyOf/0/properties/goodsId",
  170. "type": "integer"
  171. },
  172. "goodsName": {
  173. "$id": "#/properties/data/properties/recommendGoodses/items/anyOf/0/properties/goodsName",
  174. "type": "string"
  175. },
  176. "goodsIntro": {
  177. "$id": "#/properties/data/properties/recommendGoodses/items/anyOf/0/properties/goodsIntro",
  178. "type": "string"
  179. },
  180. "goodsCoverImg": {
  181. "$id": "#/properties/data/properties/recommendGoodses/items/anyOf/0/properties/goodsCoverImg",
  182. "type": "string"
  183. },
  184. "sellingPrice": {
  185. "$id": "#/properties/data/properties/recommendGoodses/items/anyOf/0/properties/sellingPrice",
  186. "type": "integer"
  187. },
  188. "tag": {
  189. "$id": "#/properties/data/properties/recommendGoodses/items/anyOf/0/properties/tag",
  190. "type": "string"
  191. }
  192. }
  193. }
  194. ]
  195. }
  196. }
  197. }
  198. }
  199. }
  200. }

编写python文件进行schema验证。
main.py



import requests
import json
from jsonschema import validate

# 读取json文件中的数据
with open('./schame.json',mode='r') as file:
    schemadata = json.load(file)

def test_index_page():

    r = requests.get("http://49.233.108.117:28019/api/v1/index-infos")
    # 添加对服务器返回的结果 进行schema 断言
    validate(instance=r.json(),schema=schemadata)

执行

pytest -s -v main.py

面试问题

  1. 你在做接口的时候是如何进行断言的?
    1. 首先对基本返回状态码进行断言
    2. 同时根据接口的业务针对主要返回结果进行断言。
    3. 也要对服务器返回结果的数据类型进行断言,数据类型使用的是 jsonschema 进行断言。