Python接口自动化框架解析

 

 

image

登录成功的接口

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()

这个是空的,用于初始化的;

image

 用于获取动态值的一个工具类,封装了一个环境变量;

# 全局变量-初始化
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

image

 

用于请求发起的一个类:

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)

image

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

image

 

image

 

image

 下面是启动类和读取报告的一个类:

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

 

posted @ 2026-06-04 16:09  贰零一八  阅读(5)  评论(0)    收藏  举报