AI自动生成接口测试脚本全流程
一、接口自动化生成自动化脚本的思路
1、接口文档发给大模型2、让大模型编写接口功能用例
3、再让大模型依据接口功能用例编写自动化用例
4、把自动化用例保存在指定目录下
二、LangChain设计
1、从完整的swagger_json中提取指定接口信息
编写代码的提示词
#背景
需要从swagger文档里面提取出对应的配置信息,根据用户输入的接口名称,提取对应的内容#需求
用户传入接口的名称列表,从swagger文档的json中找到接口名称的summary,还有接口的请求方式,path,需要把整个接口的节点的信息全部提取出来。在接口信息的response中,200节点下面有schema节点,里面引用了一个DO对象。该DO对象是在definitions节点中定义的,需要找到对应DO对象,把相关信息过滤出来#入参
1、完整的swgger_json
2、需要提取接口的名称列表summary_listswagger_json示例格式:
```json
{"swagger":"2.0","info":{"description":"卖家中心API接口","version":"7.0","title":"卖家中心Api文档"},"host":"59.36.173.55:7003","basePath":"/","tags":[{"name":"distribution-goods-seller-controller","description":"分销商品API"}],"paths":{"/seller/distribution/goods":{"put":{"tags":["distribution-goods-seller-controller"],"summary":"分销商品返利设置","operationId":"settingGoodsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"Authorization","in":"header","description":"令牌","required":false,"type":"string"},{"name":"goods_id","in":"query","description":"商品id","required":true,"type":"integer","format":"int32"},{"name":"grade1_rebate","in":"query","description":"1级提成金额","required":true,"type":"number","format":"double"},{"name":"grade2_rebate","in":"query","description":"2级提成金额","required":true,"type":"number","format":"double"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/DistributionGoods"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/seller/distribution/goods/{goods_id}":{"get":{"tags":["distribution-goods-seller-controller"],"summary":"分销商品返利获取","operationId":"querySettingUsingGET","produces":["*/*"],"parameters":[{"name":"Authorization","in":"header","description":"令牌","required":false,"type":"string"},{"name":"goods_id","in":"path","description":"商品id","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/DistributionGoods"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/seller/distribution/setting":{"get":{"tags":["distribution-goods-seller-controller"],"summary":"获取分销设置:1开启/0关闭","operationId":"settingUsingGET","produces":["*/*"],"parameters":[{"name":"Authorization","in":"header","description":"令牌","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/SuccessMessage"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},"definitions":{"DistributionGoods":{"type":"object","required":["goods_id","grade1_rebate","grade2_rebate"],"properties":{"goods_id":{"type":"integer","format":"int32","description":"商品id","allowEmptyValue":false},"grade1_rebate":{"type":"number","format":"double","description":"1级提成金额","allowEmptyValue":false},"grade2_rebate":{"type":"number","format":"double","description":"2级提成金额","allowEmptyValue":false}},"title":"DistributionGoods"},"SuccessMessage":{"type":"object","properties":{"message":{"type":"object"}},"title":"SuccessMessage"}}}
```#返回值
返回一个json,包含paths、definitions两个节点
def fetch_swagger_json(swagger_url):"""从Swagger接口获取JSON数据:param swagger_url: Swagger接口URL:return: Swagger JSON数据"""try:response = requests.get(swagger_url)response.raise_for_status() # 检查请求是否成功return response.json()except requests.exceptions.RequestException as e:print(f"获取Swagger数据失败: {e}")return Nonedef extract_swagger_info(swagger_json, summary_list):"""从Swagger JSON中提取指定接口的信息:param swagger_json: 完整的Swagger JSON文档:param summary_list: 需要提取的接口summary列表:return: 包含paths和definitions的JSON"""try:# 如果传入的是字符串,则解析为字典if isinstance(swagger_json, str):swagger_data = json.loads(swagger_json)else:swagger_data = swagger_jsonresult = {"paths": {},"definitions": {}}# 收集所有需要提取的DO对象引用do_refs = set()# 遍历paths中的所有接口for path, methods in swagger_data.get("paths", {}).items():for method, details in methods.items():if isinstance(details, dict) and details.get("summary") in summary_list:# 添加到结果中的pathsif path not in result["paths"]:result["paths"][path] = {}result["paths"][path][method] = details# 检查200响应中的schema引用responses = details.get("responses", {})if "200" in responses:schema = responses["200"].get("schema", {})if "$ref" in schema:do_refs.add(schema["$ref"])# 提取definitionsfor ref in do_refs:# 解析引用路径,如"#/definitions/DistributionGoods"parts = ref.split("/")if len(parts) == 3 and parts[1] == "definitions":do_name = parts[2]if do_name in swagger_data.get("definitions", {}):result["definitions"][do_name] = swagger_data["definitions"][do_name]return resultexcept Exception as e:return {"error": str(e)}def main(swagger_url,summary_list):# 1. 获取Swagger JSON数据swagger_json = fetch_swagger_json(swagger_url)if not swagger_json:print("无法获取Swagger数据,请检查网络或URL是否正确")return# 2. 提取指定接口信息result = extract_swagger_info(swagger_json, summary_list)# 3. 输出结果return json.dumps(result, indent=2, ensure_ascii=False)if __name__ == "__main__":api_json = main(swagger_url = "url",summary_list = [ "接口"])
2、调用LangChain生成测试用例
llm = ChatOpenAI(openai_api_base="",api_key="",temperature=0.8,max_tokens=10240,model_name="",
)def read_prompt(filename):"""读取当前脚本同级目录下的prompts文件夹中的指定文件Args:filename (str): 要读取的文件名Returns:str: 文件内容"""# 获取当前脚本所在目录current_dir = os.path.dirname(os.path.abspath(__file__))# 构建prompts文件夹路径prompts_dir = os.path.join(current_dir, 'prompts')# 构建目标文件的完整路径file_path = os.path.join(prompts_dir, filename)# 检查文件是否存在if not os.path.exists(file_path):raise FileNotFoundError(f"Prompt file '{filename}' not found in '{prompts_dir}'")# 读取文件内容with open(file_path, 'r', encoding='utf-8') as file:return file.read()def call_chain(api_json,requirements):#第一步:让大模型根据接口的json和补充需求,生成一份标准的接口需求文档prompt_template_summary = PromptTemplate(input_variables=["api_json", "requirements"],template=read_prompt("summary_prompt.md"),template_format="jinja2")#定义第一个节点summary_chain = LLMChain(llm=llm, prompt=prompt_template_summary, verbose=True, output_key="summary_result")prompt_template_summary_case = PromptTemplate(input_variables=["summary_result"],template=read_prompt("case_prompt.md"),template_format="jinja2")summary_chain_case = LLMChain(llm=llm, prompt=prompt_template_summary_case, verbose=True, output_key="api_case")#第三个节点prompt_template_script = PromptTemplate(input_variables=["api_case"],template=read_prompt("script_prompt.md"),template_format="jinja2")script_chain = LLMChain(llm=llm, prompt=prompt_template_script, verbose=True, output_key="script_result")#创建一个顺序链,把两个节点按照顺序组装all_chain = SequentialChain(chains=[summary_chain,summary_chain_case,script_chain],input_variables=["api_json","requirements"],output_variables=["api_case","script_result"],verbose=True)#运行整个链result = all_chain.invoke({"api_json":api_json,"requirements":requirements})print(result)
3、大模型根据接口测试用例生成接口自动化脚本
未完待续