keywords.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. # -*- coding: utf-8 -*-
  2. """
  3. 这是接口关键字驱动类,用于提供自动化接口测试的关键字方法。
  4. 主要是实现常用的关键字内容,并定义好所有的参数内容即可
  5. 接口中常用关键字:
  6. 1.各种模拟请求方法:Post/get/put/delete/header/....
  7. 2.根据需求进行断言封装:jsonpath、数据库断言
  8. 3.集合Allure,可添加@allure.step,这样在自动化执行的时候
  9. Allure报告可以直接捕捉相关的执行信息,让测试报告更详细
  10. """
  11. import json
  12. import random
  13. from apirun.Log.Log import logger
  14. import allure
  15. import requests
  16. import jsonpath
  17. from apirun.core.globalContext import g_context
  18. from deepdiff import DeepDiff
  19. from Crypto.Cipher import AES
  20. from Crypto.Util.Padding import pad, unpad
  21. import base64
  22. class KeyWords:
  23. request = None
  24. @allure.step("参数数据:发送Post请求")
  25. def request_post(self, **kwargs):
  26. response = requests.post(**kwargs)
  27. # TODO: 扩展- 把对应的响应数据写到变量渲染中
  28. g_context().set_dict("current_response", response) # 默认设置成变量渲染
  29. return response
  30. @allure.step("参数数据:发送Get请求")
  31. def request_get(self, **kwargs):
  32. url = kwargs.get("URL", None)
  33. params = kwargs.get("PARAMS", None)
  34. headers = kwargs.get("HEADERS", None)
  35. request_data = {
  36. "url": url,
  37. "params": params,
  38. "headers": headers,
  39. }
  40. response = requests.get(**request_data)
  41. g_context().set_dict("current_response", response)
  42. return response
  43. @allure.step("参数数据:发送Post请求-form_urlencoded")
  44. def request_post_form_urlencoded(self, **kwargs):
  45. """
  46. 发送Post请求
  47. """
  48. url = kwargs.get("URL", None)
  49. params = kwargs.get("PARAMS", None)
  50. headers = kwargs.get("HEADERS", None)
  51. data = kwargs.get("DATA", None)
  52. request_data = {
  53. "url": url,
  54. "params": params,
  55. "headers": headers,
  56. "data": data,
  57. }
  58. response = requests.post(**request_data)
  59. g_context().set_dict("current_response", response)
  60. return response
  61. @allure.step("参数数据:发送Post请求-row_json")
  62. def request_post_row_json(self, **kwargs):
  63. """
  64. 发送Post请求
  65. """
  66. url = kwargs.get("URL", None)
  67. params = kwargs.get("PARAMS", None)
  68. headers = kwargs.get("HEADERS", None)
  69. data = kwargs.get("DATA", None)
  70. request_data = {
  71. "url": url,
  72. "params": params,
  73. "headers": headers,
  74. "json": data,
  75. }
  76. response = requests.post(**request_data)
  77. g_context().set_dict("current_response", response) # 默认设置成全局变量
  78. return response
  79. @allure.step("参数数据:发送Post请求-form_data")
  80. def request_post_form_data(self, **kwargs):
  81. """
  82. 发送Post请求
  83. """
  84. url = kwargs.get("URL", None)
  85. files = kwargs.get("FILES", None)
  86. params = kwargs.get("PARAMS", None)
  87. headers = kwargs.get("HEADERS", None)
  88. data = kwargs.get("DATA", None)
  89. request_data = {
  90. "url": url,
  91. "params": params,
  92. "headers": headers,
  93. "data": data,
  94. "files": eval(files) # 变成字典格式
  95. }
  96. response = requests.post(**request_data)
  97. g_context().set_dict("current_response", response) # 默认设置成全局变量
  98. return response
  99. @allure.step("参数数据:发送Delete请求")
  100. def request_delete(self, **kwargs):
  101. """
  102. 发送Post请求
  103. """
  104. url = kwargs.get("URL", None)
  105. params = kwargs.get("PARAMS", None)
  106. headers = kwargs.get("HEADERS", None)
  107. data = kwargs.get("DATA", None)
  108. request_data = {
  109. "url": url,
  110. "params": params,
  111. "headers": headers,
  112. "json": data,
  113. }
  114. response = requests.delete(**request_data)
  115. g_context().set_dict("current_response", response) # 默认设置成全局变量
  116. return response
  117. # TODO: 扩展 - JAONPATH提取的方法
  118. @allure.step("参数数据:提取响应数据并存储")
  119. def ex_jsonData(self, **kwargs):
  120. """
  121. 提取json数据
  122. EXVALUE:提取josn的表达式
  123. INDEX: 非必填,默认为0,all代表所有
  124. VARNAME:存储的变量名,方便后面使用
  125. """
  126. # 获取JsonPath的值
  127. EXPRESSION = kwargs.get("EXVALUE", None)
  128. # 获取对应的下标,非必填,默认为0
  129. INDEX = kwargs.get("INDEX", 0)
  130. if INDEX is None:
  131. INDEX = 0
  132. # 获取响应数据
  133. response = g_context().get_dict("current_response").json()
  134. if INDEX == "all":
  135. ex_data = jsonpath.jsonpath(response, EXPRESSION)
  136. else:
  137. ex_data = jsonpath.jsonpath(response, EXPRESSION)[INDEX] # 通过JsonPath进行提取
  138. g_context().set_dict(kwargs["VARNAME"], ex_data) # 根据变量名设置成变量渲染
  139. return ex_data
  140. # TODO: 扩展 - 数据库提取的方法
  141. @allure.step("参数数据:提取数据库数据并存储")
  142. def ex_mysqlData(self, **kwargs):
  143. """
  144. 数据库 : 数据库的名称
  145. 引用变量:数据库要存储的变量名,列表格式
  146. 存储到全局变量:{“变量名_下标”:数据}
  147. """
  148. import pymysql
  149. from pymysql import cursors
  150. config = {"cursorclass": cursors.DictCursor}
  151. # 读取全局变量 - 根据选择的数据 读取指定的数据库配置 连接对应的数据库
  152. db_config = g_context().get_dict("_database")[kwargs["数据库"]]
  153. config.update(db_config)
  154. con = pymysql.connect(**config)
  155. cur = con.cursor()
  156. cur.execute(kwargs["SQL"])
  157. rs = cur.fetchall()
  158. cur.close()
  159. con.close()
  160. logger.info("数据库查询结果:", rs)
  161. var_names = kwargs["引用变量"].split(",")
  162. result = {}
  163. for i, data in enumerate(rs, start=1):
  164. for j, value in enumerate(var_names):
  165. result[f'{var_names[j]}_{i}'] = data.get(var_names[j]) # 根据变量名称找读取出来的内容
  166. g_context().set_by_dict(result)
  167. # TODO: 扩展 - 文本断言方法
  168. @allure.step("参数数据:断言当前文本内容")
  169. def assert_text_comparators(self, **kwargs):
  170. """
  171. 封装断言以进行不同的比较操作。
  172. 参数:
  173. value (Any): 要比较的值。
  174. expected (Any): 预期的值。
  175. op_str (str): 操作符的字符串表示(如 '>', '<', '==' 等)。
  176. message (str, optional): 自定义的错误消息。
  177. 返回:
  178. None: 如果断言成功,则不返回任何内容。
  179. 引发:
  180. AssertionError: 如果断言失败。
  181. """
  182. comparators = {
  183. '>': lambda a, b: a > b,
  184. '<': lambda a, b: a < b,
  185. '==': lambda a, b: a == b,
  186. '>=': lambda a, b: a >= b,
  187. '<=': lambda a, b: a <= b,
  188. '!=': lambda a, b: a != b,
  189. }
  190. message = kwargs.get("MESSAGE", None)
  191. if kwargs["OP_STR"] not in comparators:
  192. raise ValueError(f"没有该操作方式: {kwargs['OP_STR']}")
  193. if not comparators[kwargs['OP_STR']](kwargs['VALUE'], kwargs["EXPECTED"]):
  194. if message:
  195. raise AssertionError(message)
  196. else:
  197. raise AssertionError(f"{kwargs['VALUE']} {kwargs['OP_STR']} {kwargs['EXPECTED']} 失败")
  198. # TODO: 扩展 - 全量断言-对比两个Json的差异
  199. @allure.step("参数数据:全量断言-对比两个Json的差异")
  200. def assert_json_DeepDiff(self, **kwargs):
  201. """
  202. 对比两个json的差异
  203. :param json1: 期望结果
  204. :param json2: 实际结果
  205. :param exclude_paths:需要排除的字段,集合的类型,比如{“id”,...}
  206. :param ignore_order: 忽略顺序,一般用户有序数据类型,比如列表
  207. :param ignore_string_case:忽略值的大小写,False
  208. :return: 当数据没有差异则返回空集合
  209. """
  210. json1 = kwargs["json1"]
  211. json2 = kwargs["json2"]
  212. exclude_paths = kwargs.get("过滤字段", None)
  213. ignore_order = kwargs.get("忽略顺序", None)
  214. ignore_string_case = kwargs.get("忽略大小写", False)
  215. screen_data = {"exclude_paths": exclude_paths, "ignore_order": ignore_order,
  216. "ignore_string_case": ignore_string_case}
  217. diff = DeepDiff(json1, json2, **screen_data)
  218. assert not diff, f"全量断言失败:{diff}"
  219. # TODO: 扩展 - 加密处理
  220. @allure.step("参数数据:对数据进行AES加密处理")
  221. def encrypt_aes(self, **kwargs):
  222. """
  223. 对数据进行AES加密
  224. :param data: 需要加密的数据
  225. :param VARNAME: 存储到全局变量的名称
  226. :return:
  227. """
  228. key = b"1234567812345678" # key 密码
  229. data = kwargs["data"].encode('utf-8')
  230. cipher = AES.new(key, AES.MODE_ECB) # 使用ECB模式
  231. ct_bytes = cipher.encrypt(pad(data, AES.block_size)) # 使用PKCS7填充,初始化数据块大小, 16位
  232. encrypt_data = base64.b64encode(ct_bytes).decode('utf-8')
  233. g_context().set_dict(kwargs["VARNAME"], encrypt_data) # 根据变量名设置成变量
  234. # ----------------------实战扩展方法------------------------------
  235. @allure.step("参数数据:对数据进行AES加密处理")
  236. def generate_name(self, **kwargs):
  237. data = "hami" + str(random.randint(0, 9999))
  238. g_context().set_dict(kwargs["VARNAME"], data) # 根据变量名设置成变量
  239. # TODO: 扩展 - JSOND断言方法
  240. @allure.step("参数数据:JSOND断言文本内容")
  241. def assert_json_comparators(self, **kwargs):
  242. """
  243. 封装断言以进行不同的比较操作。
  244. 参数:
  245. value (Any): 要比较的jsonPath值。
  246. expected (Any): 预期的值。
  247. op_str (str): 操作符的字符串表示(如 '>', '<', '==' 等)。
  248. message (str, optional): 自定义的错误消息。
  249. 返回:
  250. None: 如果断言成功,则不返回任何内容。
  251. 引发:
  252. AssertionError: 如果断言失败。
  253. """
  254. comparators = {
  255. '>': lambda a, b: a > b,
  256. '<': lambda a, b: a < b,
  257. '==': lambda a, b: a == b,
  258. '>=': lambda a, b: a >= b,
  259. '<=': lambda a, b: a <= b,
  260. '!=': lambda a, b: a != b,
  261. }
  262. message = kwargs.get("MESSAGE", None)
  263. if kwargs["OP_STR"] not in comparators:
  264. raise ValueError(f"没有该操作方式: {kwargs['OP_STR']}")
  265. # 通过jsonpath获取对应的数据
  266. # 获取响应数据
  267. response = g_context().get_dict("current_response").json()
  268. ex_data = jsonpath.jsonpath(response, kwargs['VALUE'])[0] # 默认就取第一个
  269. if not comparators[kwargs['OP_STR']](ex_data, kwargs["EXPECTED"]):
  270. if message:
  271. raise AssertionError(message)
  272. else:
  273. raise AssertionError(f"{ex_data} {kwargs['OP_STR']} {kwargs['EXPECTED']} 失败")