zoukankan      html  css  js  c++  java
  • 接口自动化框架pyface详细介绍

    版权说明

    本框架系本人结合一些实践经验和开源框架设计思想,在家基于兴趣爱好独立完成的代码开发。

    源码只保存在私人电脑,办公电脑上无。github开源与公司无关,先把关系撇清,不涉及侵权。

    嘘。

    框架定位

    首先最重要的就是学习交流,无商业用途。其次本框架有一定实用价值,可作为工作辅助工具,解决现有技术无法处理的问题。最后可以优化改造投入生产实用(若有更好的idea请务必告诉我,求知若渴)。

    设计思想

    技术栈

    说明文字为本框架中用途。

    python:脚本语言。

    requests:http请求库。

    allure:测试报告

    numpy:数据格式兼容。

    pandas:mysql返回数据处理。

    PyMySQL:连接mysql。

    SQLAlchemy:mysql连接引擎,支持ORM。

    texttable:日志打印sql查询结果表格。

    目录结构

    用例组织方式

    模板代码使用code_auto.py自动生成。

            self.api_dir = os.path.join(os.path.join(self.base_dir, 'api'), 'bu')  # 1
            self.case_dir = os.path.join(os.path.join(self.base_dir, 'case'), 'sprint')  # 2
            self.uri = '/api/post'  # 3
            self.description = 'Demo auto code'  # 4
            # 5
            self.body = """{}
    """
    

    1 输入api子目录名称。接口是按业务部门来区分的,子目录名称建议按业务部门(bu==Business Unit)来设置。

    2 输入测试用例子目录名称。现在流行敏捷开发,建议按迭代sprint或独立功能模块命名。

    3 接口uri。需要注意的是,开头要有1个斜杠/

    4 接口描述。如名称、作用。

    5 请求体。

    执行后在api和case目录生成测试初始化代码。

    域名、Headers/Cookie涉及到环境变量,在data/env设置。

    class _GldExp:
        x = 1
        headers = {"Content-Type": "application/json"}
        dao_x = Dao('host:port',
                    'username',
                    'password')
    
        test_url = 'https://x'
    
    
    class _Gld:
        x = 2
        headers = {"Content-Type": "application/json"}
        dao_x = Dao('host:port',
                    "username",
                    "password")
    
        test_url = 'https://x'
    
    
    def uuid_list(n):
        """Uuid list
    
        @param n: Number
        @return: A uuid list
        """
        return [str(uuid.uuid4()).replace('-', '') for i in range(n)]
    
    
    # Set environment name
    vars_ = _GldExp
    

    2个内部类_GldExp_Gld,定义参数化环境变量。

    在env文件中可以定义一些业务相关函数。公共函数需要放到common/func,建议不要轻易把框架无关的函数放到公共函数里面。

    import env后,使用vars_引用来调用具体的环境变量,如vars_.test_url

    测试代码编写方式

    api/bu目录下,每个接口==1个py文件。

    class ApiPost(Api):
    
        def __init__(self):
            super().__init__()
            self.url = vars_.test_url + "/api/post"
    
        def load(self):
            self.body = {}
    
            return self
    
        def send(self):
            self.res = self.req.post(url=self.url, headers=vars_.headers, json=self.body)
            self.set_content()
            return self.res
    

    api继承了基类Api。根据不同环境初始化vars_.test_urlload()方法用于加载参数,send()方法用于发送请求(视不同method修改对应的请求方法&参数,如get,可以在common/request.py中找到相关定义)。

    测试代码完全面向对象。

    def test_default():
        x = ApiPost()
        x.load().send()
    

    这样能很方便的在接口之间传递参数,以及做参数化的工作。

    比如,在接口.py中,需要参数化body的name:

        def load(self):
            self.body = {
                "name": self.name
            }
    

    PyCharm会提示此属性未定义,忽略它。

    在测试代码中写参数化就很简单:

        x.name = 'dongfanger'
        x.load().send()
    

    JMeter参数化方式

    本框架参数化借鉴了JMeter的参数化方式。也就是,在接口发请求后,对参数赋值;在接口收到相应后,提取参数。这也是测试代码要完全面向对象的原因。

    面向对象能较好的组织测试代码,使代码逻辑清晰,阅读易于理解。

    比如,先定义2个接口,苹果树和商店:

    class AppleTree(Api):
    
        def __init__(self):
            super().__init__()
            self.url = vars_.test_url + "/api/post/apple/tree"
    
        def load(self):
            self.body = {}
    
            return self
    
        def send(self):
            self.res = self.req.post(url=self.url, headers=vars_.headers, json=self.body)
            self.set_content()
            return self.res
    
    class ShopSale(Api):
    
        def __init__(self):
            super().__init__()
            self.url = vars_.test_url + "/api/post/shop/sale"
    
        def load(self):
            self.body = {
                "apple": self.apple
            }
    
            return self
    
        def send(self):
            self.res = self.req.post(url=self.url, headers=vars_.headers, json=self.body)
            self.set_content()
            return self.res
    

    测试代码编写,苹果树先生产苹果,再运输到商店,商店卖苹果:

    def test_apple_to_shop():
        apple_tree = AppleTree()
        apple_tree.load().send()  # 生产苹果
        good_apple = apple_tree.content['good_apple']  # content在Api基类中定义
        shop_sale = ShopSale()
        shop_sale.apple = good_apple  # 传递参数
        shop_sale.load().send()
        print(shop_sale.content)
    

    content在Api基类中定义:

        def set_content(self):
            """After request, assert status and set content
    
            """
            status_ok(self.res)
            res_json = self.res.json()
            assert 1000 == res_json.get('status')
            try:
                self.content = res_json['content']
            except KeyError:
                logger.info(f"{'*' * 26}
    "
                            f"Response no content
    "
                            f"{'*' * 26}
    ")
    

    先断言返回状态ok,再取响应json里面key为content的value。不同公司json规范不一样,需要做调整。

    批量执行用例生成测试报告

    pytest_allure.py批量执行测试用例。

    # Input the directory to run pytest
    run_dir = os.path.join(base_dir, 'case')
    

    默认执行case目录下test_开头或结尾的文件(pytest规则)。测试方法需要以test_开头。

    可以指定目录,如:

    # Input the directory to run pytest
    run_dir = os.path.join(os.path.join(base_dir, 'case'), 'sprint0001')
    

    本框架借助pytest_sessionfinish hook函数实现了生成测试报告并自动打开浏览器。

    def pytest_sessionfinish(session):
        allure_report_dir_test = session.config.getoption('allure_report_dir')
        if allure_report_dir_test:
            html_dir = os.path.join(allure_report_dir_test, 'html')
            os.system(f'mkdir {html_dir}')
            os.system(f"allure generate {allure_report_dir_test} -o {html_dir}")
            os.system(f"allure open {html_dir}")
    

    mysql支持

    mysql主要用于:一提供参数化赋值;二数据库比对断言。

    commons/dao.py实现了相关功能。在data/env.py中根据环境定义好连接后,通过vars_使用。

        dao_x = Dao('host:port',
                    'username',
                    'password')
    
        sql_result = vars_.dao_x.select('select id, name from new_table;')
    

    dao实现采用了pandas+sqlalchemy,对返回结果取值就可以按dataframe来,如sql_result['name'][0]

    借助texttable会打印表格日志,观察数据。

    [2020-03-22 18:14:13]Running sql
    select id, name from new_table;
    
    [2020-03-22 18:14:14]Sql result:
    +----+------+
    | id | name |
    +====+======+
    | 1  | w    |
    +----+------+
    | 2  | g    |
    +----+------+
    

    值得说明的是,为了数据校验方便,默认会把无小数的float转换为int,如5.0->5

        @staticmethod
        def _convert(x):
            """Convert logic code
    
            @param x: Single cell data
            @return: Converted single cell data
            """
            # float to int
            if isinstance(x, float) and x % 1 == 0:
                return int(x)
            return x
    

    结语

    开源使我快乐。

    分享才能收获更多。

    我在github等你。

    https://github.com/dongfanger/pyface
    版权申明:本文为博主原创文章,转载请保留原文链接及作者。

  • 相关阅读:
    mysql字符集
    zabbix自动发现zabbix_agent后添加到所属组和链接到某些模块(九)
    zabbix_agent添加到系统服务启动(八)
    苦苦的追寻,奈何~~(一年三份工作的总结)
    systemd service 设置limit,不生效问题
    logrotate
    cron定时任务
    crontab定时任务第一个周期未完成下一个周期执行就来了
    来年仍旧迷茫...
    rsync的daemon模式
  • 原文地址:https://www.cnblogs.com/df888/p/12592716.html
Copyright © 2011-2022 走看看