ExcelCaseParser.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. # -*- coding: utf-8 -*-
  2. import ast
  3. import copy
  4. import json
  5. import os.path
  6. # 专门用例Excel参数化
  7. # 导入excel的包:pip install pandas
  8. # openpyxl是pandas用于读取.xlsx文件的引擎之一
  9. # pip install pandas openpyxl
  10. # 读取yaml的数据
  11. import yaml, os, uuid
  12. import pandas as pd
  13. from apirun.core.globalContext import g_context
  14. from apirun.Log.Log import logger
  15. def load_context_from_excel(folder_path):
  16. """
  17. :param folder_path: 文件路径
  18. :return:
  19. """
  20. try:
  21. excel_file_path = os.path.join(folder_path, "context.xlsx") # 把2个文本进行拼接
  22. # 读取excel
  23. df = pd.read_excel(excel_file_path)
  24. # 初始化一个变量进行存储
  25. data = {
  26. "_database": {}
  27. }
  28. for index, row in df.iterrows():
  29. # 如果是变量我们就直接存在:变量名:value
  30. if row["类型"] == "变量":
  31. data[row["变量描述"]] = row["变量值"]
  32. elif row["类型"] == "数据库":
  33. db_name = row["变量描述"]
  34. db_config = json.loads(row["变量值"]) # 变成字典格式
  35. data["_database"][db_name] = db_config
  36. if data: g_context().set_by_dict(data) # 写入到全局变量
  37. except Exception as e:
  38. logger.error(f"装载excel文件错误: {str(e)}")
  39. return False
  40. # 加载我们满足条件的文件及数据
  41. def load_excel_files(config_path):
  42. """
  43. 返回满足条件的excel文件列表及数据
  44. :param config_path: excel存放的路径
  45. :return:
  46. """
  47. excel_caseInfos = [] # 存储所有的数据
  48. suite_folder = os.path.join(config_path)
  49. # 存放在该路径的全局变量进行写入
  50. load_context_from_excel(suite_folder)
  51. # 满足条件的列表
  52. file_names = [(int(f.split("_")[0]), f) for f in os.listdir(suite_folder) if
  53. f.endswith(".xlsx") and f.split("_")[0].isdigit()]
  54. # 排序,排序只保留文件名即可
  55. file_names.sort()
  56. file_names = [f[-1] for f in file_names]
  57. # 读取我们维护的yaml数据,方便后面和值一一对应起来
  58. current_dir = os.path.abspath(os.path.dirname(__file__)) # 获取当前文件的绝对路径
  59. parent_dir = os.path.dirname(current_dir) # 基于当前路径获取上一层路径
  60. keywords_file_path = os.path.join(parent_dir, "extend/keywords.yaml") # 进行拼接
  61. keywords_info = {}
  62. with open(keywords_file_path, "r", encoding="utf-8") as f:
  63. keywords_info = yaml.full_load(f) #
  64. # 加载每个文件的数据给到yaml_caseInfos
  65. for file_name in file_names:
  66. file_path = os.path.join(suite_folder, file_name)
  67. # TODO 1: 读取excel
  68. lr =[]
  69. for i in pd.ExcelFile(file_path).sheet_names: # 遍历excel表格的sheet页名称
  70. if i != '关键字说明':
  71. data = pd.read_excel(file_path, sheet_name=i) # 获取单个sheet页的全量数据
  72. data = data.where(data.notnull(), None) # 将非空值保留,空数据用None替换
  73. data = data.to_dict(orient='records') # 用一个大列表把没一行编成一个字典元素,存储起来
  74. lr.extend(data)
  75. # 初始化一个字典,用例存放某一条测试用例
  76. current_test_case = None
  77. for row in lr:
  78. # 判断是否有测试标题:如果有的话,代表是我们第一个,直到遇到下一个标题,就从头开头
  79. # TODO 1: # 检查当前行是否包含有效的测试用例标题,有则代表对应的起始位
  80. if pd.notna(row["测试用例标题"]):
  81. # 如果已经构建完毕那么需要当前加进去到对应的测试用例当中
  82. if current_test_case is not None:
  83. excel_caseInfos.append(current_test_case)
  84. # 初始化一个测试用例字典
  85. current_test_case = {
  86. "process": row['进程类'],
  87. "desc": row['测试用例标题'],
  88. "featureName": row['一级模块'],
  89. "storyName": row['二级模块'],
  90. "steps": []
  91. }
  92. # TODO 2-1: 初始化步骤的值
  93. steps = {
  94. row["步骤描述"]: {
  95. "关键字": row["关键字"]
  96. }
  97. }
  98. # TODO 2-2: 考虑步骤当中对应的每个参数。
  99. parameter = []
  100. for key, value in row.items():
  101. if "参数_" in key:
  102. try:
  103. # 尝试将字符串转换为Python对象
  104. value = ast.literal_eval(value)
  105. except:
  106. pass
  107. parameter.append(value)
  108. # TODO 2-3: 把对应的值和KEY一一对应起来,这样我们才能发送请求 -zip
  109. dict_parameter = {k: v for k, v in zip(keywords_info[row["关键字"]], parameter)}
  110. steps[row["步骤描述"]].update(dict_parameter)
  111. # 步骤加到当前测试用例来
  112. current_test_case["steps"].append(steps)
  113. if current_test_case is not None:
  114. excel_caseInfos.append(current_test_case)
  115. return excel_caseInfos
  116. # 把yaml的格式处理成我们规定的格式
  117. def excel_case_parser(config_path):
  118. """
  119. 返回指定条件的格式
  120. :param config_path: 文件的路径
  121. :return: {
  122. "case_infos": case_infos, # 所有的测试用例 []
  123. "case_names": case_names # 所有测试用例对应的标题 []
  124. }
  125. """
  126. # TODO 0: 对应固定的格式
  127. case_infos = []
  128. case_names = []
  129. case_process = []
  130. # TODO 1: 调用对应的方法:拿到所有的用例数据
  131. excel_caseInfos = load_excel_files(config_path)
  132. # TODO 2: 统一处理成一样的格式 (后面excel一样的处理)
  133. for caseinfo in excel_caseInfos:
  134. caseinfo.update({"_case_name": caseinfo["desc"]})
  135. case_infos.append(caseinfo) # 所有的测试用例 []
  136. case_names.append(caseinfo["desc"]) # 所有的用例名称 []
  137. case_process.append(caseinfo["process"]) # 所有的用例使用的进程 []
  138. return {
  139. "case_infos": case_infos,
  140. "case_names": case_names,
  141. "case_process": case_process
  142. }
  143. # data = excel_case_parser(r"D:\001_zzh\eng\api-engine\examples\examples-dsw")
  144. # print(data)