Python接口自动化框架解析

登录成功的接口
from common.http_requests import * from common.global_variable import * class Login(HttpRequests): def login_devops(self, user, password): # 登录 logger.info("登录DEVOPS") url = f"https://signinuat.midea.com/login?service=http%3A%2F%2Fdevsecopsuat.midea.com%2Fagile%2Ftest%2Ffunction%2Fcase%3FsysId%3DITS000001096&locale=zh" self._https_get(url=url) data = f'username={user}&password={password}&execution=e1s1&bVisitorId=5e96ed57febc37d079725bd8f40ea042&_eventId=submit&authType=sms123&geolocation=' resp = self._https_post_form(url=url, data=data) set_global_variable("DevopsSession", self.session) return resp.text
登录的前置接口和获取系统页面调用的接口
from common.http_requests import * from common.global_variable import * class Sample(HttpRequests): def __init__(self): self.session = get_global_variable("DevopsSession") if self.session is None: logger.error("未登录devops系统") raise SystemExit def xxx_api(self, data): url = "https://devsecopsuat.midea.com/api/system-project-server/systemInfo/getListPage" resp = self._https_post_json(url=url, data=data) return resp.json()
新增用例的接口调用
from common.http_requests import * from common.global_variable import * class NewCase(HttpRequests): def __init__(self): self.session = get_global_variable("DevopsSession") if self.session is None: logger.error("未登录devops系统") raise SystemExit def new_Case(self,params): # 点击新建用例按钮 url = "https://devsecopsuat.midea.com/api/agile-service/workItem/v1/getItemView/VIRTUAL_SYSTEM_TESTCASE_TEMPLATE_ID" resp=self._https_get(url=url, params=params) return resp.json()
删除用例的接口调用,中间涉及到Gpath表达式的调用
from common.http_requests import * from common.global_variable import * class DeleteCase(HttpRequests): def __init__(self): self.session = get_global_variable("DevopsSession") if self.session is None: logger.error("未登录devops系统") raise SystemExit #@pytest.mark.parametrize(params,body) def Delete_Case(self,params): # url = "https://devsecopsuat.midea.com/api/agile-service/test/delete/AAAU059924/ITS000001096" #要加一下headers headers = {"Teamid": "", "Orgid": "bbe42cba6adb4d268d72ce087123e4c5", "Systemid": "ITS000001096", "View": "system"} url = f"https://devsecopsuat.midea.com/api/agile-service/test/delete/{get_global_variable('body')}/ITS000001096" print("url=",url) resp = self._https_get(url,data=params,headers=headers) return resp.json()
这个是空的,用于初始化的;

用于获取动态值的一个工具类,封装了一个环境变量;
# 全局变量-初始化 def init_global_variable(): global _global_dict _global_dict = {} # 设置全局变量 def set_global_variable(key, value): try: # 框架使用的全局变量不打印日志 _global_dict[key] = value except Exception: try: init_global_variable() _global_dict[key] = value except Exception as e: raise Exception(f"设置全局变量:{key}={value},异常:{e}") # 获得一个全局变量,如果不存在则返回None def get_global_variable(key): try: return _global_dict[key] except Exception as e: # 默认sit环境 if key == "keepENV": set_global_variable("keepENV", "sit") return "sit" print(f"获取全局变量:【{key}】异常:{e}") return None
封装Http请求的一个类:
import json import requests from loguru import logger class HttpRequests: session = requests.session() def _https_get(self, url, params=None, data=None, headers=None): """ get请求 :param url: 请求地址 :param headers: 非必输,报文头 :param data: 非必输,请求报文体 :return:返回响应内容 """ logger.debug(f"请求报文:{data}") self.headers = {"Connection": "keep-alive", "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", "Accept": "application/json, text/javascript, */*; q=0.01"} if headers is not None: self.headers.update(headers) resp = self.session.request(url=url, method="GET", params=params, data=data, headers=self.headers, allow_redirects=True, timeout=180) logger.debug(f"响应报文:{resp.text}") return resp def _https_post_form(self, url, params=None, data=None, headers=None): """ 请求报文格式为form的post请求 :param url: 请求地址 :param headers: 非必输,报文头 :param data: 非必输,请求报文体 :return:返回响应内容 """ logger.debug(f"请求报文:{data}") self.headers = {"Connection": "keep-alive", "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", "Accept": "application/json, text/javascript, */*; q=0.01"} if headers is not None: self.headers.update(headers) resp = self.session.request(url=url, method="POST", params=params, data=data, headers=self.headers, allow_redirects=True, timeout=180) logger.debug(f"响应报文:{resp.text}") return resp def _https_post_json(self, url, params=None, data=None, headers=None): """ 请求报文格式为json的post请求 :param url: 请求地址 :param headers: 非必输,报文头 :param data: 非必输,请求报文体 :return:返回响应内容 """ if isinstance(data, dict): data = json.dumps(data) logger.debug(f"请求报文:{data}") self.headers = {"Connection": "keep-alive", "Content-Type": "application/json; charset=utf-8", "Accept": "application/json, text/javascript, */*; q=0.01"} if headers is not None: self.headers.update(headers) resp = self.session.request(url=url, method="POST", params=params, data=data, headers=self.headers, allow_redirects=True, timeout=180) logger.debug(f"响应报文:{resp.text}") return resp
读取请求入参的一个工具类,用于读取yaml;
import os, yaml, re BASE_DIR = os.path.dirname(os.path.realpath(__file__)) ROOT_PATH = os.path.abspath(os.path.join(BASE_DIR, "../")) # 读取单个yaml文件内的数据 def read_yaml(yamlPath): BASE_DIR = os.path.dirname(os.path.realpath(__file__)) root_path = os.path.abspath(os.path.join(BASE_DIR, "../")) yamlPath = root_path + yamlPath with open(yamlPath, encoding='utf-8') as f: # 读取yaml当中的数据 data = yaml.safe_load(f) return data def create_yaml(sendData, yamlFile): """ 将字典、列表生成yaml文件 :param sendData: 传入请求报文(字典、列表) :param yamlFile: 生成yaml文件名 """ try: yamlFile = os.path.join(ROOT_PATH + yamlFile) with open(yamlFile, "w", encoding="utf-8") as f: yaml.dump(sendData, f, allow_unicode=True) except Exception as e: raise Exception(f"生成YAML文件错误:{e}") if __name__ == '__main__': pass # 请求报文:可以是数组、字典、字符串 sendata = {"param":{"org_id":"bbe42cba6adb4d268d72ce087123e4c5","sys_name":""},"current":1,"size":10,"startPage":1,"pageSize":10} # 调用创建yaml方法 create_yaml(sendata, "/test_data/sample.yaml")
用于生成测试报告的一个工具类:
# -*- coding: utf-8 -*- # @File : shell.py import subprocess class Shell: """封装执行shell语句方法""" @staticmethod def invoke(cmd): output, errors = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() o = output.decode("utf-8") return o

用于请求发起的一个类:
import allure from apis.SaveCase import SaveCase from apis.checkCase import CheckCase from apis.copyCase import CopyCase from apis.deleteCase import DeleteCase from apis.exportCase import ExportCase from apis.login import * from apis.sample import * from apis.newCase import NewCase from common.read_yaml import * class TestClass01: #登录 @allure.feature('登录') @allure.story('登录') def test_1(self): # BlazeMeter抓包 Login().login_devops("ex_shijl2", "vi3fYRFnStfYLXBriYRLlg==") print("登录成功") sample = Sample() data = read_yaml("/test_data/sample.yaml") sample.xxx_api(data) #点击新建用例按钮 @allure.feature('点击新建用例') @allure.story('点击新建用例') def test_2(self): newCase = NewCase() data = read_yaml("/test_data/newCase01.yaml") newCase.new_Case(data) #点击【保存】按钮,中间会调用检查用例的接口 @allure.feature('校验唯一性用例') @allure.story('校验唯一性用例') def test_3(self): checkCase = CheckCase() data = read_yaml("/test_data/checkCase.yaml") checkCase.Check_Case(data) @allure.feature('保存用例') @allure.story('保存用例') def test_301(self): saveCase = SaveCase() data = read_yaml("/test_data/saveCase.yaml") saveCase.Save_Case(data) #复制 @allure.feature('复制用例') @allure.story('复制用例') def test_4(self): copyCase = CopyCase() data = read_yaml("/test_data/copyCase01.yaml") copyCase.copy_Case(data) @allure.feature('保存复制的用例') @allure.story('保存复制的用例') def test_301(self): saveCase = SaveCase() data = read_yaml("/test_data/saveCopyCase.yaml") saveCase.Save_Case(data) #导出 @allure.feature('导出用例') @allure.story('导出用例') def test_5(self): #类名首字母要大写,yaml里面的数据不要格式化; #Login().login_devops("ex_shijl2", "vi3fYRFnStfYLXBriYRLlg==") exportCase = ExportCase() exportCase.get_system_info({"systemId":"ITS000001096","listView":"drop_down_tree","extMap":{}}) data = read_yaml("/test_data/exportCase.yaml") resp = exportCase.export_Case(data) assert resp.status_code == 200 # with open("./test.xls", 'wb') as f: # f.write(resp.content) #删除 @allure.feature('删除用例') @allure.story('删除用例') # allure serve ./result/ # allure serve ./allure-results def testDelete(self): deleteCase=DeleteCase() data = read_yaml("/test_data/deleteCase.yaml") deleteCase.Delete_Case(data)

请求入参的一个yaml文件的实际读取配置



下面是启动类和读取报告的一个类:
import pytest, os, sys from common.shell import Shell from loguru import logger # 运行系统标识 systemMark = str(sys.platform) if __name__ == "__main__": BASE_DIR = os.path.dirname(os.path.realpath(__file__)) root_path = os.path.abspath(os.path.join(BASE_DIR)) # 构建一个cmd运行 shell = Shell() allure_report = "--alluredir=./allure-results" allure_result_path = "./allure-results" allure_report_path = "./allure-report" try: # 执行用例 args = ['-v', '-s', 'test_case/', allure_report, '--clean-alluredir', "--reruns=1"] pytest.main(args) # 生成allure报告 cmd = 'allure generate %s -o %s --clean' % (allure_result_path, allure_report_path) shell.invoke(cmd) logger.debug("生成allure报告成功") except Exception as e: logger.error(f'执行用例失败,{e}')
allure-pytest==2.9.45
allure-python-commons==2.9.45
jmespath==0.9.5
jsonpath==0.82
logger==1.4
numpy==1.24.3
openpyxl==3.0.10
orjson==3.7.7
pandas==2.0.1
pytest==7.1.2
pytest-dependency==0.6.0
pytest-forked==1.4.0
pytest-html==3.1.1
pytest-metadata==2.0.1
pytest-rerunfailures==10.2
pytest-sugar==0.9.4
pytest-xdist==2.5.0
python-dateutil==2.8.2
PyYAML==6.0
regex==2023.8.8
requests==2.27.1
setuptools==56.0.0
simple-settings==1.2.0
six==1.16.0
xlrd==2.0.1
成年人的世界没有那么多的童话,也没有那么多的逆袭。

浙公网安备 33010602011771号