捕获.PNG

以下仅纸上谈兵。

3.1 设计excel结构

根据个人需要设计

case编号 用例名称 是否执行 是否是链路 前置条件 依赖key url或链路名称 method 请求参数 header操作 预期结果方式 预期结果 执行结果 返回数据
Aha_001 名称1 yes /login/phone post {“phone”: “15xxxxxx”,

“password”: “123456”} | yes | code_msg | | pass | | | Aha_002 | 名称2 | no | yes | Aha_001>data.pageData.[0].id | projectId | upload_user_photo | get | | | code | 200 | | | | Aha_003 | 名称3 | yes | no | Aha_004>data.userId | | /userInfo/ | | | | json | | failed | |

3.2 excel操作

写得时候需要调一调,就是+1或者-1的区别。。因为有的时候从0开始,有的时候从1开始

  1. # coding=utf-8
  2. import openpyxl
  3. import sys
  4. import os
  5. sys.path.append(os.path.realpath('..'))
  6. class OperateExcel:
  7. def load_excel(self):
  8. """
  9. 加载excel
  10. """
  11. open_excel = openpyxl.load_workbook(r"../Case/case.xlsx")
  12. return open_excel
  13. def get_sheet_data(self, index=None):
  14. """
  15. 加载所有sheet的内容
  16. """
  17. sheet_name = self.load_excel().sheetnames
  18. if index is None:
  19. index = 0
  20. data = self.load_excel()[sheet_name[index]]
  21. return data
  22. def get_cell_value(self, row, cols):
  23. """
  24. 获取某一个单元格的内容
  25. """
  26. data = self.get_sheet_data().cell(row=row, column=cols).value
  27. return data
  28. def get_rows(self):
  29. """
  30. 获取行数
  31. """
  32. row = self.get_sheet_data().max_row
  33. return row-1
  34. def get_row_value(self, row):
  35. """
  36. 获取某行内容
  37. """
  38. value_list = []
  39. for i in self.get_sheet_data()[row]:
  40. value_list.append(i.value)
  41. return value_list
  42. def write_data(self, row, cols, value):
  43. """
  44. 写入数据
  45. :param row:
  46. :param cols:
  47. :param value:
  48. :return:
  49. """
  50. wb = self.load_excel()
  51. wr = wb.active
  52. wr.cell(row, cols, value)
  53. wb.save(r"../Case/case.xlsx")
  54. def get_columns_data(self, col_no=None):
  55. """
  56. 获取某列的所有值
  57. :param col_no: 列号, str
  58. :return:
  59. """
  60. columns_list = []
  61. if col_no is None:
  62. col_no = 'A'
  63. columns_list_data = self.get_sheet_data()[col_no]
  64. for i in columns_list_data:
  65. columns_list.append(i.value)
  66. return columns_list
  67. def get_row_number(self, case_id):
  68. """
  69. 获取行号
  70. :return:
  71. """
  72. number = 1
  73. cols_data = self.get_columns_data()
  74. for col_data in cols_data:
  75. if case_id == col_data:
  76. return number
  77. number = number + 1
  78. def get_excel_data(self):
  79. """
  80. 获取excel里所有的数据
  81. :return:
  82. """
  83. data_list = []
  84. for i in range(self.get_rows()):
  85. # 从1开始数,所以第二行是2
  86. data_list.append(self.get_row_value(i+2))
  87. return data_list
  88. # 单例模式,只需实例化一次
  89. excel_data = OperateExcel()
  90. if __name__ == "__main__":
  91. excel = OperateExcel()
  92. print(excel.get_row_number("imooc_002"))

3.3 ini文件操作

ini文件就是配置文件,记录一些常用参数比方说host地址

  1. [server]
  2. host=https://aha.cn/aha_test
  3. phone=15xxxxxxxxx
  4. password=123456
  5. [case]
  6. case_no=0
  7. case_name=1
  8. is_run=2
  9. is_link=3
  10. is_depend=4
  11. depend_key=5
  12. url=6
  13. method=7
  14. request_data=8
  15. header_method=9
  16. expect_method=10
  17. expect_result=11
  18. result=12
  19. res_data=13
  20. [pic_file1]
  21. file_name = p130865532.jpg
  22. file_path = C:\Users\zero\PycharmProjects\auto_interface_test\File
  23. file_type = image/jpeg

读取ini文件

  1. # coding = utf-8
  2. import os
  3. import sys
  4. sys.path.append(os.path.realpath('..'))
  5. import configparser
  6. class ReadIni:
  7. def load_ini(self):
  8. file_path = r'..\Config\server.ini'
  9. cf = configparser.ConfigParser()
  10. cf.read(file_path, encoding="utf-8-sig")
  11. return cf
  12. def get_value(self, key, section=None):
  13. """
  14. 获取ini里的值
  15. :param key:
  16. :param section:
  17. :return:
  18. """
  19. if section is None:
  20. section = 'server'
  21. cf = self.load_ini()
  22. try:
  23. value = cf.get(section, key)
  24. except Exception as e:
  25. print("没有获取到值")
  26. value = None
  27. print(e)
  28. return value
  29. return value
  30. read_ini = ReadIni()
  31. if __name__ == "__main__":
  32. ri = ReadIni()
  33. data = ri.get_value('result', 'case')
  34. print(data)

3.4 run main

几个重点。

3.4.1 前置条件

首先要搞清楚有几种请求
如果是post和有参数的get请求,就需要三个信息。比方说发送评论,需要先获取到评论id,评论id又是通过上一个请求“获取所有评论”获得的。所以就是三个信息:上一请求的返回参数,提取参数的规则,依赖参数。就是表格中的返回数据、前置条件、依赖参数。(名称自己瞎起的,领会精神)
如果是不带参数的get请求,比方说host/project/23这样的url,就不需要有依赖参数,拼接一下url就可以了。
那么代码中就是:

  1. if is_depend is not None:
  2. depend_data = load_dependency(is_depend)
  3. if depend_key is not None:
  4. request_data[depend_key] = depend_data
  5. print(request_data)
  6. else:
  7. url = url + str(depend_data)

提取所需的前置参数值,precondition.py
规则是imooc_001>data:banner:id
先使用get_dependent_data获取imooc_001这条用例中的返回数据集,然后通过后面的规则通过get_dependent_value提取所需的参数值,然后整合到load_dependency中方便调用。

  1. # coding = utf-8
  2. import os
  3. import sys
  4. from jsonpath_rw import parse
  5. import json
  6. sys.path.append(os.path.realpath('..'))
  7. from Util.operate_excel import excel_data
  8. def split_data(data):
  9. """
  10. :param data: excel中的内容
  11. :return:
  12. """
  13. # data = imooc_001>data:banner:id
  14. case_id = data.split(">")[0]
  15. data_rule = data.split(">")[1]
  16. return case_id, data_rule
  17. def get_dependent_data(data):
  18. """
  19. 从excel中获取数据集
  20. :param data: excel中的内容
  21. :return:
  22. """
  23. case_id = split_data(data)[0]
  24. row_number = excel_data.get_row_number(case_id)
  25. row_value = excel_data.get_cell_value(row_number, 14)
  26. return row_value
  27. def get_dependent_value(data, rule):
  28. """
  29. 从数据集中提取所需的字段
  30. :param data: 请求返回的数据
  31. :param rule: 匹配规则:data.banner.[0].id
  32. :return:
  33. """
  34. data = json.loads(data)
  35. for i in parse(rule).find(data):
  36. return i.value
  37. #return [i.value for i in parse(rule).find(data)][0]
  38. def load_dependency(data):
  39. response_data = get_dependent_data(data)
  40. rule = split_data(data)[1]
  41. return get_dependent_value(response_data, rule)

3.4.2 链路

(这部分是自己瞎写的)
因为有一些用例需要通过多个请求完成,所以把这些请求整合到一个方法中,直接调用方法。
我使用反射完成。

  1. if is_link is not None:
  2. #url=upload_user_photo
  3. execute_link = getattr(link, url)
  4. res = execute_link(phone, password, file_name, file_path, file_type)

就是先新建一个link.py文件,调用里面的方法。
下面用上传头像举例:

  1. # coding = utf-8
  2. import os
  3. import sys
  4. sys.path.append(os.path.realpath('..'))
  5. import requests
  6. from Base.base_request import request
  7. from requests.packages.urllib3.exceptions import InsecureRequestWarning
  8. requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  9. class Link:
  10. def upload_user_photo(*args):
  11. """
  12. 上传头像
  13. :param phone:
  14. :param password:
  15. :param filename:
  16. :param path:
  17. :param pic_type: image/jpeg
  18. :return:
  19. """
  20. # 登录
  21. proxies = {'http': "http://localHost:8888", 'https': 'http://localhost:8888'}
  22. headers = {'Content-Type': 'application/json'}
  23. login_url = "/login/phone"
  24. data1 = {'phone': args[1], 'password': args[2]}
  25. res_login = request.run_main('post', login_url, data1, headers, proxies, False)
  26. print(res_login)
  27. token = res_login['data']['token']
  28. headers['Authorization'] = token
  29. # 获取向COS上传公共文件签名
  30. signature_url = "/userInfo/avatar/sign/upload"
  31. data2 = {
  32. "filename": args[3]
  33. }
  34. res_signature = request.run_main('get', signature_url, data2, headers, proxies, False)
  35. print(res_signature)
  36. key = res_signature['data']['filename']
  37. policy = res_signature['data']['policy']
  38. q_ak = res_signature['data']['secretId']
  39. q_key_time = res_signature['data']['keyTime']
  40. q_sign_algorithm = "sha1"
  41. q_signature = res_signature['data']['signature']
  42. # 上传图片到服务器
  43. pic_url = "https://aha-public-1257019972.cos.ap-shanghai.myqcloud.com"
  44. with open(args[4], "rb") as f_abs:
  45. body = {
  46. "key": (None, key),
  47. "policy": (None, policy),
  48. "q-ak": (None, q_ak),
  49. "q-key-time": (None, q_key_time),
  50. "q-sign-algorithm": (None, q_sign_algorithm),
  51. "q-signature": (None, q_signature),
  52. "file": (args[3], f_abs, args[5])
  53. }
  54. res_pic = requests.post(pic_url, files=body, proxies=proxies, verify=False)
  55. print(res_pic.headers)
  56. location = res_pic.headers['Location']
  57. # 修改用户信息
  58. modify_user_info_url = "/userInfo/me"
  59. modify_user_info_data = {
  60. "avatarUrl": location
  61. }
  62. res_put = request.run_main("put", modify_user_info_url, modify_user_info_data, headers, proxies, False)
  63. print(res_put)
  64. return res_put

3.4.3 判断case是否通过

首先要知道有几种判断方法,可以通过code_msg、code、json结构判断。

  1. if expect_method == 'code_msg':
  2. config_message = read_expected_msg(url, code)
  3. if message == config_message:
  4. print("测试case通过")
  5. excel_data.write_data(i + 2, int(read_ini.get_value('result', 'case'))+1, "pass")
  6. excel_data.write_data(i + 2, int(read_ini.get_value('res_data', 'case'))+1, json.dumps(res))
  7. else:
  8. print("测试case失败")
  9. excel_data.write_data(i + 2, int(read_ini.get_value('result', 'case'))+1, "fail")
  10. excel_data.write_data(i + 2, int(read_ini.get_value('res_data', 'case'))+1, json.dumps(res))
  11. if expect_method == 'code':
  12. if expect_result == code:
  13. print("测试case通过")
  14. excel_data.write_data(i + 2, int(read_ini.get_value('result', 'case'))+1, "pass")
  15. excel_data.write_data(i + 2, int(read_ini.get_value('res_data', 'case'))+1, json.dumps(res))
  16. else:
  17. print("测试case失败")
  18. excel_data.write_data(i + 2, int(read_ini.get_value('result', 'case'))+1, "fail")
  19. excel_data.write_data(i + 2, int(read_ini.get_value('res_data', 'case'))+1, json.dumps(res))
  20. if expect_method == 'json':
  21. if code == "200":
  22. status = "success"
  23. else:
  24. status = "error"
  25. expect_result = get_result_json(url, status)
  26. if check_json_format(res, expect_result):
  27. print("测试case通过")
  28. excel_data.write_data(i + 2, int(read_ini.get_value('result', 'case'))+1, "pass")
  29. excel_data.write_data(i + 2, int(read_ini.get_value('res_data', 'case'))+1, json.dumps(res))
  30. else:
  31. print("测试case失败")
  32. excel_data.write_data(i + 2, int(read_ini.get_value('result', 'case'))+1, "fail")
  33. excel_data.write_data(i + 2, int(read_ini.get_value('res_data', 'case'))+1, json.dumps(res))
  34. if expect_method is None:
  35. excel_data.write_data(i + 2, int(read_ini.get_value('res_data', 'case')) + 1, json.dumps(res))

a. code_msg

举个栗子。就是状态码和返回信息之间的对应关系。
code_msg.json

  1. "/login/phone": [
  2. {"200": "登录成功"},
  3. {"302": "用户名或密码错误!"},
  4. {"301": "用户不存在!"},
  5. {"701": "访问次数超限!"}

问题就是如何提取,和判断是否对应。

  1. def read_expected_msg(url, code):
  2. data = get_value(url, "code_message.json")
  3. if data is not None:
  4. # data是一个list
  5. for i in data:
  6. message = i.get(code)
  7. if message is not None:
  8. return message
  9. return None

b. code

这个就是提取返回结果中的code和表格中的预期code对比

c. json结构

这个就是看返回的结果的json结构是否和预期的json结构一致,所以也是有一个json文件记录预期json结构。
expect_result.json
根据状态(success/error)区分预期的结果
image.png

  1. def get_result_json(url, status):
  2. data = get_value(url, "expect_result.json")
  3. if data is not None:
  4. # data是一个list
  5. for i in data:
  6. message = i.get(status)
  7. if message is not None:
  8. return message
  9. return None
  10. def check_json_format(dict1, dict2):
  11. """
  12. 校验json格式
  13. :return:
  14. """
  15. # 参数必须为字典,如果有一个不是字典,就返回false
  16. if isinstance(dict1, dict) and isinstance(dict2, dict):
  17. cmp_dirt = DeepDiff(dict1, dict2, ignore_order=True).to_dict()
  18. #print(cmp_dirt)
  19. if cmp_dirt.get("dictionary_item_added"):
  20. return False
  21. else:
  22. return True
  23. return False

未完待续,其实我不是很清楚如何管理case。。。