如何实现一个业务系统的自动化框架搭建

1、框架结构

我在该项目采用的是关键字驱动测试的框架类型。首先创建如下几个目录common(公共模块)、config(公共配置)、logs(运行日志)、reports(测试报告)、resources(测试资源)、testcases(测试用例)、utils(工具脚本)。在接下来的环节我将详细介绍这几个模块的实现。

如何实现一个业务系统的自动化框架搭建_第1张图片

2、公共配置config 

config文件夹下放有config.ini和conf.py。config.ini用来管理多套测试环境(dev、sit、uat),config.py用来管理项目目录的存取。

# config.ini
[WEB_SIT]
sit = icare
type = uat
sms_url = http://xx.sangfor.com
url = http://xx.sangfor.com
username = user
password = pass
db = sf-icare
db_host = 1.1.1.1
db_port = 3306
db_user = xx
db_pass = xx

[ICARE_DEV]
sit = icare
type = dev
sms_url = http://xx.sangfor.com
url = http://xx.sangfor.com
username = xx
password = xx
db = sf-icare
db_host = 2.2.2.2
db_port = 3306
db_user = xx
db_pass = xx

[ICARE_SIT]
sit = icare
type = sit
sms_url = http://xx.sangfor.com
url = http://xx.sangfor.com
username = xx
password = xx
db = sf-icare
db_host = 3.3.3.3
db_port = 3306
db_user = xx
db_pass = xx

[ICARE_UAT]
sit = icare
type = uat
sms_url = http://xx.sangfor.com
url = http://xx.sangfor.com
username = xx
password = xx
db = sf-icare
db_host = 4.4.4.4
db_port = 3306
db_user = xx
db_pass = xx

[PRM_SIT]
sit = prm
type = sit
url = https://xx.sangfor.com
username = xx
password = xx

[TOKEN]
token = /api/api-auth/oauth/user/token

[HEADERS]
user_agent = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
authorization = Bearer 4521eb70-8fb2-4747-a512-1d2aed596849

[ICARE_USER]
super_user = 13300,99896,94406,98923,43913,21401

#conf.py
import os
import uuid
from utils.times import dt_strftime


class ConfigManager(object):
    # 项目目录
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


    @property
    def log_file(self):
        """日志目录"""
        log_dir = os.path.join(self.BASE_DIR, 'logs', dt_strftime())
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)
        return os.path.join(log_dir, '{}.log'.format(dt_strftime('%Y%m%d')))

    @property
    def ini_file(self):
        """配置文件"""
        ini_file = os.path.join(self.BASE_DIR, 'config', 'config.ini')
        if not os.path.exists(ini_file):
            raise FileNotFoundError("配置文件%s不存在!" % ini_file)
        return ini_file

    @property
    def report_dir(self):
        """测试报告目录"""
        report_dir = os.path.join(self.BASE_DIR, 'reports', dt_strftime('%Y%m%d'), str(uuid.uuid1()))
        if not os.path.exists(report_dir):
            os.makedirs(report_dir)
        report_json_dir = os.path.join(report_dir, 'temp_jsonreport')
        report_html_dir = os.path.join(report_dir, 'html')
        if not os.path.exists(report_json_dir):
            os.makedirs(report_json_dir)
        if not os.path.exists(report_html_dir):
            os.makedirs(report_html_dir)
        dir_dict = {'report_json_dir': report_json_dir, 'report_html_dir':report_html_dir}
        return dir_dict


    def json_file(self, json_name):
        """json文件"""
        json_file = os.path.join(self.BASE_DIR, 'resources', json_name)
        if not os.path.exists(json_file):
            raise FileNotFoundError("json文件%s不存在!" % json_file)
        return json_file

    def test_file(self, test):
        """上传下载测试文件"""
        test = os.path.join(self.BASE_DIR, 'resources', test)
        if not os.path.exists(test):
            raise FileNotFoundError("测试文件%s不存在!" % test)
        return test



cm = ConfigManager()
if __name__ == '__main__':
    cm.report_html_dir
    # pass

3、公共模块common

common目录下主要存放着各个业务模块的关键字封装(cass_step)和一些公用的场景,如连接数据库、读取配置文件内容、获取登录用户token、request请求封装。如下摘取片段:

#  question_step.py
  @allure.step("setup:检查问题单基本信息")
    def check_q_info(self, re_info, q_data):
        """
        1、取问题单的基本信息
        2、取提交问题单时填写的基本信息
        3、对比
        :param re_info:get_question_info
        :param q_data: {
            "客户名称": "测试",
            "客户联系人": "小李",
            "客户联系电话": "13343427921",
            "产品线": "aCMP",
            "业务影响": "P21",
            "问题分类": "产品技术问题",
            "问题描述": "描述测试测试测试",
        }
        :return:
        """

        try:
            cust_name = re_info["customerName"]
            contact_name = re_info["contactName"]
            contact_phone = re_info["contactPhoneNumber"]
            product = re_info["mainProductLine"]
            descrip = re_info["questionDescription"]

            cs.assert_result("包含", q_data["客户名称"], cust_name)
            cs.assert_result("等于", contact_name, q_data["客户联系人"])
            cs.assert_result("等于", contact_phone, q_data["客户联系电话"])
            cs.assert_result("等于", product, q_data["产品线"])
            cs.assert_result("等于", descrip, q_data["问题描述"])

        except TypeError:
            pytest.fail("该工单不存在!")
        except AssertionError:
            pytest.fail("检查工单信息和填写不一致!")
# request_url.py
class RequestMain:
    def __init__(self):
        self.session = requests.Session()

    def request_main(self, method, url, params=None, data=None, payloads=None, headers=None, redirects=True, **kwargs):
        """
        :param method: 请求方式
        :param url: 请求地址
        :param params: 字典或bytes,作为参数增加到url中
        :param data: data类型传参,字典、字节序列或文件对象,作为Request的内容
        :param payloads: json传参,作为Request的内容
        :param headers: 请求头,字典
        :param kwargs: 若还有其他的参数,使用可变参数字典形式进行传递
        :return:
        """
        ini = ReadConfig()
        ini_headers = {
            'User-Agent': ini.user_agent,
            'Authorization': ini.auth,
        }
        # log.info(ini)

        if headers is None:
            self.session.headers.update(ini_headers)
        else:
            self.session.headers.update(headers)
        # log.info('正在请求{}接口:{}'.format(method, url))

        try:
            """
            封装request请求,将请求方法、请求地址,请求参数、请求头等信息入参。
            注 :verify: True/False,默认为True,认证SSL证书开关;cert: 本地SSL证书。如果不需要ssl认证,可将这两个入参去掉
                allow_redirects:True/False,默认为True,是启动重定向
            """
            re_data = self.session.request(method, url, params=params, data=data, json=payloads, headers=headers, allow_redirects=redirects, timeout=10, verify=False, **kwargs)
            try:
                log.info(self.session.headers["Content-Type"])
                del self.session.headers['Content-Type']
            except KeyError:
                pass

            if re_data.status_code != 200:
                # 如果重定向,新连接在response headers里
                if re_data.status_code == 302:
                    return re_data.headers
                raise Exception(re_data.text)
            result = re_data.text
            # log.info("请求成功,返回结果{0}".format(result))
            return json.loads(result)
        except asyncio.exceptions.TimeoutError:
            log.error("连接超时!")
            pytest.fail("连接超时")
        except Exception as e:
            log.error("请求失败:{0}".format(e))
            pytest.fail("请求失败:{0}".format(e))

 4、运行日志logs

记录运行过程的日志,用来调试用例。

INFO	2023-09-11 10:32:02,611	[question_step.py:135]	问题单创建成功:Q2023091100004
INFO	2023-09-11 10:32:02,723	[question_step.py:248]	问题单当前状态为{'main_status': 20, 'status': 2010}
INFO	2023-09-11 10:32:02,729	[question_step.py:285]	获取问题创建人为:w13300
INFO	2023-09-11 10:32:02,785	[common_step.py:62]	当前登陆人:w13300
INFO	2023-09-11 10:32:03,035	[conftest.py:71]	恢复配置中...

5、测试资源resources

用来存放测试使用的接口数据json

6、测试用例testcases

存放测试用例(按业务场景串联起来的关键字)

# test_question.py    
    @pytest.mark.smoke
    @allure.feature('问题管理')
    @allure.title('新建问题单自己处理流程')
    def test_question_my(self):
        # 1、手动新建问题单,填写基本信息,选自己处理 - 请求受理,提交
        q_no = qs.create_question(q_info, deal_type={"type": "自己处理", "info": "请求受理"})
        q_data = qs.get_question_info(q_no)
        # 2、检查问题单状态、责任人、问题来源
        status = qs.get_q_status(q_data)
        cs.assert_result("等于", status["status"], ts.trans_question_status("请求受理")[1])
        cs.assert_result("等于", status["main_status"], ts.trans_question_status("请求受理")[0])
        create_user = qs.get_question_handler(q_data, "问题创建人")
        user1 = cs.login_user()["username"]
        cs.assert_result("等于", create_user, user1)
        # 3、检查问题单基本信息,客户名称、产品线、业务影响、版本号、问题描述、问题分类
        qs.check_q_info(q_data, q_info)
        # 4、写进展 - 问题确认解决,检查处理进展,检查短信
        qs.write_next_plan(q_no, "问题确认解决")
        # 5、点击短信里的超链接,检查H5页面
        sms_dict = qs.get_sms_record(q_no)
        cs.assert_result("大于", sms_dict["count"], 0)
        sms_link = sms_dict["content"]
        short_key = qs.request_sms_link(sms_link)
        h5_progress_count = qs.h5_show_progress(short_key)
        cs.assert_result("等于", h5_progress_count, 3)
        # 6、H5页面评价,勾选四星提交,检查后台评价结果
        score = 8
        qs.sms_evaluation(q_no, score)
        web_score = qs.get_eval_result(q_no)
        cs.assert_result("等于", score, web_score)

 7、测试报告allure

使用了allure报告,看起来更美观。

如何实现一个业务系统的自动化框架搭建_第2张图片如何实现一个业务系统的自动化框架搭建_第3张图片

8、工具脚本utils

为了测试用例的代码更简洁美观,将处理数据、字段取值转换等单独封装起来。

9、运行自动化测试

创建解析器,自定义命令行参数,让执行更简单

# main.py
def get_command():
    """
    创建一个解析器,解析命令行参数
    :return: args
    """
    parser = argparse.ArgumentParser(description="解析命令行参数")
    parser.add_argument("--env", "-e", help="环境配置-dev/sit/uat")
    parser.add_argument("--module", "-m", help="用例模块-问题模块/项目模块")
    parser.add_argument("--smoke", "-s", help="冒烟执行-1")
    parser.add_argument("--test", "-t", help="测试调试-1")
    args = parser.parse_args()
    return args


    # 命令行执行python .\main.py -e sit -m 问题模块 -s 1
    # 参数-e对应测试环境:可输入dev/sit/uat,不填默认sit
    # 参数-m对应测试模块:可输入问题模块/项目模块,不填默认执行全部
    # 参数-s对应冒烟测试:可输入1,不填默认执行全部

你可能感兴趣的:(自动化,运维)