Yapi接口文档导出测试用例至Excel中
前言
因为项目要求测试用例必须是excel,而且Yapi导出选项中没有excel,所以写个脚本方便以后一键生成。
实现
利用python实现
import jsonimport pandas as pd
import requestsclass YapiToExcel:def __init__(self, yapi_url, token, cookies, project_name, use_test_case):self.yapi_url = yapi_url.rstrip('/')self.token = tokenself.headers = {'Content-Type': 'application/json','Authorization': f'{token}'}self.cookies = cookiesself.project_name = project_nameself.use_test_case = use_test_casedef _generate_request_body_example(self, schema):"""根据JSON Schema生成请求体示例"""if not isinstance(schema, dict) or schema.get('type') != 'object':return json.dumps(schema, ensure_ascii=False, indent=2)properties = schema.get('properties', {})required = schema.get('required', [])# 构建示例请求体example_body = {}for prop_name, prop_info in properties.items():if not isinstance(prop_info, dict):continue# 获取属性类型prop_type = prop_info.get('type', 'string')# 优先使用mock值mock_info = prop_info.get('mock', {})if mock_info and 'mock' in mock_info:example_body[prop_name] = mock_info['mock']else:# 根据类型填充默认值if prop_type == 'string':example_body[prop_name] = ""elif prop_type == 'integer':example_body[prop_name] = 0elif prop_type == 'number':example_body[prop_name] = 0elif prop_type == 'boolean':example_body[prop_name] = Falseelif prop_type == 'array':example_body[prop_name] = []elif prop_type == 'object':example_body[prop_name] = {}else:example_body[prop_name] = ""return json.dumps(example_body, ensure_ascii=False, indent=2)def get_project_list(self):"""获取项目列表"""url = f"{self.yapi_url}/api/project/list"params = {'page': 1,'limit': 100,'token': self.token}response = requests.get(url, headers=self.headers, params=params)result = response.json()if result and result.get('errcode') == 0:return result.get('data', {})return {}def get_interface_list(self, project_id):"""获取项目下的接口列表"""url = f"{self.yapi_url}/api/interface/list"all_interfaces = []page = 1limit = 30 # 每页获取10条数据while True:params = {'project_id': project_id,'token': self.token,'page': page,'limit': limit}response = requests.get(url, params=params)result = response.json()if result and result.get('errcode') == 0:data = result.get('data', {})interfaces = data.get('list', [])all_interfaces.extend(interfaces)# 获取总数和当前数据数量total_count = data.get('count', 0)current_count = len(all_interfaces)# 如果已获取的数据量大于等于总数,或者当前页没有数据,则停止循环if not interfaces or current_count >= total_count:breakpage += 1else:breakreturn all_interfacesdef get_interface_detail(self, interface_id):"""获取接口详情"""url = f"{self.yapi_url}/api/interface/get"params = {'id': interface_id,'token': self.token}response = requests.get(url, params=params)result = response.json()if result and result.get('errcode') == 0:return result.get('data', {})return {}def get_test_case_list(self, project_id):"""获取项目下的测试用例集合列表"""url = f"{self.yapi_url}/api/col/list"params = {'project_id': project_id,# 'token': self.token}response = requests.get(url, params=params, cookies=self.cookies)result = response.json()if result and result.get('errcode') == 0:return result.get('data', [])return []def get_test_case_detail(self, test_case_id):"""获取测试用例详情"""url = f"{self.yapi_url}/api/col/case"params = {'caseid': test_case_id,'token': self.token}response = requests.get(url, params=params, cookies=self.cookies)result = response.json()if result and result.get('errcode') == 0:return result.get('data', {})return {}def extract_interface_case_data(self, interface_detail):"""提取测试用例数据"""if not interface_detail:return Noneparsed_body = None # 或者根据业务逻辑设置合适的默认值# 请求路径path = interface_detail.get('path', '')# 请求方法method = interface_detail.get('method', '')# 请求头req_headers = interface_detail.get('req_headers', [])headers_str = json.dumps(req_headers, ensure_ascii=False) if req_headers else ''# 请求参数req_params = interface_detail.get('req_params', [])req_query = interface_detail.get('req_query', [])req_body = interface_detail.get('req_body_other', '') or interface_detail.get('req_body_form', '')req_body_example = "" # 用于存储生成的请求体示例# if req_body:# try:# # 如果req_body是字符串形式的JSON,解析并格式化# if isinstance(req_body, str):# parsed_body = json.loads(req_body)# req_body = json.dumps(parsed_body, ensure_ascii=False, indent=2)# # 如果req_body已经是dict格式,直接格式化# elif isinstance(req_body, dict):# req_body = json.dumps(req_body, ensure_ascii=False, indent=2)# except json.JSONDecodeError:# # 如果解析失败,保持原始数据# passif req_body:try:# 如果req_body是字符串形式的JSON,解析if isinstance(req_body, str):parsed_body = json.loads(req_body)# 如果req_body已经是dict格式elif isinstance(req_body, dict):parsed_body = req_body# 判断是否为JSON Schema格式if isinstance(parsed_body, dict) and 'type' in parsed_body and 'properties' in parsed_body:# 生成真实的请求体示例req_body_example = self._generate_request_body_example(parsed_body)# 保留原始Schema作为备用req_body = json.dumps(parsed_body, ensure_ascii=False, indent=2)else:# 普通JSON数据,格式化显示req_body_example = json.dumps(parsed_body, ensure_ascii=False, indent=2)req_body = req_body_exampleexcept json.JSONDecodeError:# 如果解析失败,保持原始数据pass# 如果没有生成示例请求体,则使用处理后的req_bodyif not req_body_example:req_body_example = req_body# 合并所有请求参数params_info = {'req_params': req_params,'req_query': req_query,'req_body': req_body}params_str = json.dumps(params_info, ensure_ascii=False)# 返回结果res_body = interface_detail.get('res_body', '')return {'项目名称': self.project_name,'测试用例ID': interface_detail.get('_id', ''),'测试用例名称': interface_detail.get('title', ''),'测试用例描述': '','请求路径': path,'请求方法': method,# 'Header头': headers_str,'请求query': req_query,'请求param': req_params,'请求body': req_body_example,'预期结果': res_body,'创建时间': interface_detail.get('add_time', ''),'更新时间': interface_detail.get('up_time', '')}def extract_test_collection_data(self, test_case_detail, project_name=""):"""提取测试集合数据"""if not test_case_detail:return None# 提取测试用例基本信息case_name = test_case_detail.get('casename', '')case_desc = test_case_detail.get('desc', '')path = test_case_detail.get('path', '')method = test_case_detail.get('method', '')# 请求头headers = test_case_detail.get('req_headers', [])headers_str = json.dumps(headers, ensure_ascii=False) if headers else ''# 请求头headers = test_case_detail.get('req_headers', [])headers_str = json.dumps(headers, ensure_ascii=False, indent=2) if headers else ''# 请求参数params = test_case_detail.get('req_query', [])params_str = json.dumps(params, ensure_ascii=False, indent=2) if params else ''req_query = test_case_detail.get('req_query', [])# 请求体数据req_body = test_case_detail.get('req_body_other', '')if req_body:try:# 如果req_body是字符串形式的JSON,解析并格式化if isinstance(req_body, str):parsed_body = json.loads(req_body)req_body = json.dumps(parsed_body, ensure_ascii=False, indent=2)# 如果req_body已经是dict格式,直接格式化elif isinstance(req_body, dict):req_body = json.dumps(req_body, ensure_ascii=False, indent=2)except json.JSONDecodeError:# 如果解析失败,保持原始数据pass# 预期响应res_body = test_case_detail.get('res_body', '')return {'项目名称': self.project_name,'测试用例ID': test_case_detail.get('_id', ''),'测试用例名称': case_name,'测试用例描述': case_desc,'请求路径': path,'请求方法': method,# 'Header头': headers_str,'请求query': req_query,'请求param': params_str,'请求body': req_body,'预期结果': res_body,'创建时间': test_case_detail.get('add_time', ''),'更新时间': test_case_detail.get('up_time', ''),}def export_project_to_excel(self, project_id, output_file):"""导出项目接口到Excel"""if self.use_test_case:all_test_cases = []test_cases_list = self.get_test_case_list(project_id)if test_cases_list:for test_cases in test_cases_list:for test_case in test_cases.get('caseList'):test_case_id = test_case.get('_id')if test_case_id:# 获取测试用例详情detail = self.get_test_case_detail(test_case_id)test_collection_data = self.extract_test_collection_data(detail, self.project_name)if test_collection_data:all_test_cases.append(test_collection_data)# 创建DataFrame并导出到Excelif all_test_cases:df = pd.DataFrame(all_test_cases)df.to_excel(output_file, index=False)print(f"成功导出 {len(all_test_cases)} 个接口到 {output_file}")else:print("没有找到有效的接口数据")else:# 获取接口列表interfaces = self.get_interface_list(project_id)if not interfaces:print("获取接口列表失败")returntest_cases = []for interface in interfaces:interface_id = interface.get('_id')if interface_id:# 获取接口详情detail = self.get_interface_detail(interface_id)test_case_data = self.extract_interface_case_data(detail)if test_case_data:test_cases.append(test_case_data)# 创建DataFrame并导出到Excelif test_cases:df = pd.DataFrame(test_cases)df.to_excel(output_file, index=False)print(f"成功导出 {len(test_cases)} 个接口到 {output_file}")else:print("没有找到有效的接口数据")def export_all_projects_to_excel(self, output_file):"""导出所有项目接口到Excel"""# 获取项目列表project_data = self.get_project_list()if not project_data:print("获取项目列表失败")returnall_test_cases = []projects = project_data.get('list', [])for project in projects:project_id = project.get('_id')project_name = project.get('name')print(f"正在处理项目: {project_name}")# 获取接口列表interfaces = self.get_interface_list(project_id)if not interfaces:continuefor interface in interfaces:interface_id = interface.get('_id')if interface_id:# 获取接口详情detail = self.get_interface_detail(interface_id)test_case_data = self.extract_interface_case_data(detail)if test_case_data:test_case_data['项目名称'] = project_nameall_test_cases.append(test_case_data)# 获取测试用例列表test_cases = self.get_test_case_list(project_id)if test_cases:for test_case in test_cases:test_case_id = test_case.get('_id')if test_case_id:# 获取测试用例详情detail = self.get_test_case_detail(test_case_id)test_collection_data = self.extract_test_collection_data(detail, project_name)if test_collection_data:all_test_cases.append(test_collection_data)# 创建DataFrame并导出到Excelif all_test_cases:df = pd.DataFrame(all_test_cases)# 调整列顺序,将项目名称放在前面cols = df.columns.tolist()if '项目名称' in cols:cols.remove('项目名称')cols.insert(0, '项目名称')df = df[cols]df.to_excel(output_file, index=False)print(f"成功导出 {len(all_test_cases)} 个接口到 {output_file}")else:print("没有找到有效的接口数据")def _generate_request_body_example(self, schema):"""根据JSON Schema生成请求体示例"""if not isinstance(schema, dict) or schema.get('type') != 'object':return json.dumps(schema, ensure_ascii=False, indent=2)properties = schema.get('properties', {})required = schema.get('required', [])# 构建示例请求体example_body = {}for prop_name, prop_info in properties.items():if not isinstance(prop_info, dict):continue# 获取属性类型prop_type = prop_info.get('type', 'string')# 优先使用mock值mock_info = prop_info.get('mock', {})if mock_info and 'mock' in mock_info:example_body[prop_name] = mock_info['mock']else:# 根据类型填充默认值if prop_type == 'string':example_body[prop_name] = ""elif prop_type == 'integer':example_body[prop_name] = 0elif prop_type == 'number':example_body[prop_name] = 0elif prop_type == 'boolean':example_body[prop_name] = Falseelif prop_type == 'array':example_body[prop_name] = []elif prop_type == 'object':example_body[prop_name] = {}else:example_body[prop_name] = ""return json.dumps(example_body, ensure_ascii=False, indent=2)def main():# 直接定义常量YAPI_URL = "http://11.11.11.11:3000/" # 替换为你的Yapi服务器地址TOKEN = "1234" # 替换为你的Yapi访问tokenPROJECT_ID = 2680 # 项目ID,如果为None则导出所有项目OUTPUT_FILE = "yapi_test_cases.xlsx" # 输出Excel文件名# 如果要导出Yapi测试用例集合那么需要去F12 查看cookiescookies = {'_yapi_token': "1234",'_yapi_uid': "1243"}project_name = "国内食材管理"use_test_case = True # 如果想导出Yapi中测试用例集合那么设置为Trueyapi_client = YapiToExcel(YAPI_URL, TOKEN, cookies, project_name=project_name, use_test_case=use_test_case)if PROJECT_ID:yapi_client.export_project_to_excel(PROJECT_ID, OUTPUT_FILE)else:yapi_client.export_all_projects_to_excel(OUTPUT_FILE)if __name__ == "__main__":main()