【Robot Framework 项目实战 03】使用脚本自动生成统一格式的RF自动化用例

背景

虽然大家都已经使用了统一的关键字,但是在检查了一些测试用例之后,还是发现因为大家对RF的熟悉程度不一导致的测试用例颗粒度差异很大的情况;而且在手动方式转化测试用例过程中,有不少工作是完全重复的且意义不大的,所以我就想了一个使用脚本生成自动化测试用例的方式来解决这个问题。

实现思路

前一篇文章里提到过,我们使用了一个中间的Excel来生成关键字信息,所以我这边的第一想法就是让这个Excel发挥更大的作用 -- 即直接通过Excel生成标准的测试用例。
具体的思路也很简单:

  • 直接转化我们填写在Excel中的请求参数与预期结果。
    • 以递归的形式处理多层级的数据,存储到一个列表中,然后倒序写到robot文件中
      最关键的代码如下:

发送数据转化

请求的数据如下:

{
"name": "Detector",
 "age": 18,
 "sex": "male", 
"student": false, 
"others": [{"nation": "CHINA"}]
}

我们需要对数据逐层进行解析,然后生成下面的这个数据)。

${others}    create dictionary    nation=CHINA    
${others_list}    create list    ${others}
${param}    create dictionary    name=Detector    age=${18}    sex=male    student=${False}    others=${others_list} 

注: RF脚本中所有的非特殊标示的数据皆为string,bool类型及int类型需要用${something}包裹

因为请求数据是逐行执行,所以所有的参数定义都需要逐行进行赋值,受限于RF的语法,这一块花了不少时间,最后使用两个函数相互递归的方式解决。

_format_list用于处理list, _format_dict用于处理dict, temp_list用于存储生成的关键字信息,最后使用'\n'.join(temp_list) + "\n"把所有的子关键字组装成RF关键字代码。

相关代码:

代码目录结构如下:

def _gen_param_data(self, sheet_obj, param_key="param"):
    """
    获取Excel中发送参数的数据
    :param sheet_obj:
    :param param_key:
    :return:
    """
    str_params = sheet_obj.cell_value(1, 1)
    str_method = sheet_obj.cell_value(1, 6)
    if not len(str_params):
        return ""
    try:
        # 转化 json类型为Python标准类型
        params = eval(str_params.replace("false", "False").replace("true", "True").replace("null", "None"))
        if not len(params):
            return ""
    except Exception as f:
        logger.warning("====================================================")
        logger.error("=" + str(f))
        logger.warning("====================================================")
        params = ""
    if str_method.upper() == "GET":
        # 格式化get请求
        format_str = self.format_get_params(params, param_key)
    else:
        # 格式化post请求
        format_str = self.format_post_params(params)

    return format_str

def format_post_params(self,  params):
    """
    格式化post请求传递数据
    :param params:
    :return:
    """
    temp = []
    # 格式化数据
    if isinstance(params, dict):
        self._format_dict(params, "param", temp)
        temp.reverse()
    if isinstance(params, list):
        print("list")
        self._format_list(params, "param", temp)
        temp.reverse()
    return '\n'.join(temp) + "\n"

def _format_list(self, _list, key_word, temp_list):
    tem_str = "    ${%s_list}    create list    " % key_word
    if len(_list) > 0 and isinstance(_list, list):
        for i in _list:
            if isinstance(i, dict):
                tem_str = "    ${%s_list}    create list    ${%s}" % (key_word, key_word)
                self._format_dict(i, key_word, temp_list)
            elif isinstance(i, list):
                self._format_list(i, key_word, temp_list)
            else:
                tem_str += self.format_bool_int(i) + "    "
        temp_list.insert(0, tem_str)

def _format_dict(self, _dict, key_word, temp_list):
    tem_str = "    ${%s}    create dictionary    " % key_word
    if len(_dict) > 0 and isinstance(_dict, dict):
        for k, v in _dict.items():
            v = self.format_bool_int(v)

            if isinstance(v, str):
                tem_str += k + "=" + v + "    "

            if isinstance(v, dict):
                tem_str += k + "=" + "${%s_dict}    " % k
                self._format_dict(v, "%s_dict" % k, temp_list)

            if isinstance(v, list):
                tem_str += k + "=" + "${%s_list}    " % k
                self._format_list(v, k, temp_list)
        temp_list.insert(0, tem_str)
        
def format_get_params(params, param_key):
    """
    格式化get请求传递数据
    :param params:
    :param param_key:
    :return:
    """
    format_str = "    ${%s}        set variable     ?" % param_key
    if isinstance(params, dict):
        for k, v in params.items():
            if isinstance(v, int) or isinstance(v, bool):
                v = "${%s}" % v
            format_str = format_str + str(k) + "=" + str(v) + "&"
    if isinstance(params, list):
        pass    # 后续完善

    return format_str.strip("&") + '\n'

用例初始化

首先,我们把每条用的引用数据写到文件中。其中/env/env.robot用于存放我们的配置文件。

def gen_testcase_init(target_robot_name):
    """
    测试用例初始化内容
    :param target_robot_name:
    :return:
    """
    if os.path.exists(target_robot_name):
        os.remove(target_robot_name)

    with open(target_robot_name, 'a') as f:
        f.write('*** Settings ***' + '\n')
        f.write('Documentation     documentation' + '\n')
        f.write('Suite Setup       Setup func' + '\n')
        f.write('Suite Teardown    Teardown func' + '\n')
        f.write('Test Setup        log    Test Setup' + '\n')
        f.write('Test Teardown     log    Teardown' + '\n')
        f.write('Resource          ../../../Common/DT_Hb_kw/DT_Hb_kwRequests.robot' + '\n')
        f.write('Resource          ./env/env.robot' + '\n')
        # f.write('Library           TestLibrary' + '\n')
        f.write('\n')

        f.write('*** Variables ***' + '\n')
        f.write('${OK}     OK' + '\n')
        f.write('\n')
        f.write('*** Test Cases ***' + '\n')

前置条件及数据清理

def gen_end_keyword(target_robot_name):
    """
    测试用例初始化内容
    :param target_robot_name:
    :return:
    """
    with open(target_robot_name, 'a') as f:
        f.write('*** Keywords ***' + '\n')
        f.write('Setup func' + '\n')
        f.write('    log    setup func' + '\n')
        f.write('Teardown func' + '\n')
        f.write('    log    teardown func' + '\n')
        f.write('\n')
apitest/
├── Common
│   ├── DT_Hb_kw  # 公共RF关键字存储目录
│   │   └── DT_Hb_kwRequests.robot
│   ├── DT_Hb_kw_excel  # 公共过程Excel存储目录
│   │   ├── detector.xls
│   │   ├── detector_Get.xls
│   │   ├── detector_getBingo.xls
│   │   ├── detector_post.xls
│   │   └── detector_postName.xls
│   └── Testscript  # 辅助脚本存储目录
│       ├── DT_Hb_kwRequests.robot
│       ├── common
│       │   ├── __init__.py
│       │   ├── gen_rf_demo_case.py
│       │   ├── gen_rf_kw.py
│       │   ├── gen_testcase.py
│       │   ├── har_parse.py
│       ├── common_testcase  # 普通用例存储目录
│       │   └── 
│       ├── display
│       │   ├── client.py
│       │   ├── ipsitter.xls
│       │   └── server.py
│       ├── har_files   # har文件存储目录
│       │   └── 20190812-Demo.har
│       ├── logs
│       │   └── 2019-08-16.log
│       ├── rf_demo_cases  # 生成的自动化用例存储
│       ├── run.py
│       ├── source_xls
│       │   ├── new_keyword_excel  # 用于生成关键字的Excel存储目录
│       │   └── templates  # 模板Excel存储目录
│       │       ├── case_template.xls
│       │       └── kw_template.xls
│       └── utils
│           ├── __init__.py
│           ├── logger.py
│           └── operate_xls.py
├── Testcase
│   ├── DT_Hb_case
│   │   ├── Demo_server  # 测试用例提交处
│   │   ├── Post_Demo  # 测试用例提交处
│   │   └── _common  # 项目公共关键字存储目录
│   │       ├── commonFun.robot
│   │       ├── common_func.py
│   │       └── env.robot

posted @ 2019-02-02 14:19  Bingo-he  阅读(2068)  评论(0编辑  收藏  举报