zoukankan      html  css  js  c++  java
  • 测试框架学习之HttpRunner测试用例之用例分层设计(八)

    一、测试用例分层目的
    解决测试场景增多,导致接口定义描述的维护困难和繁琐问题。
    例如,某个项目中存在三个测试场景(引用官网的示例)
    场景A:注册新账号(API_1 / 2)、登录新注册的账号(API_3 / 4 / 5)、查看登录状态(API_6);
    场景B:登录已有账号(API_3 / 4 / 5)、注销登录(API_7 / 8);
    场景C:注销登录(API_7 / 8)、查看登录状态(API_6)、注册新账号(API_1 / 2)。

    常规用例设计

       用例分析
    场景A和场景C同时定义了接口(API_1 / 2 / 6)
    场景A和场景B同时定义了接口(API_3 / 4 / 5)
    场景B和场景C同时定义了接口(API_7 / 8)

    用例分析结果:出现反复定义同类接口,对于后续测试场景增加,接口定义描述的维护就会变得非常困难和繁琐

    二、测试用例分层设计

    用例封装

    出现重复代码块,将其封装为类或方法,然后在需要时进行调用,以此来消除重复。同样地,我们也可以将项目的API进行统一定义,里面包含API的请求和预期响应描述,然后在测试场景中进行引用即可。
    示意图如下所示


    如上图所示,场景A和场景C都包含了注册新账号(API_1 / 2)和查看登录状态(API_6),场景A和场景B都包含了登录已有账号(API_3 / 4 / 5),场景B和场景C都包含了注销登录(API_7 / 8)。
    虽然我们已经将接口的定义描述抽离出来,避免了重复的定义;但是在实际业务场景中,某些功能(例如登录、注销)会在多个场景中重复出现,而该功能又涉及到多个接口的组合调用,这同样也会出现大量的重复。


    模块化封装
    常用功能封装为模块(suite),只需要在模块中定义一次,然后就可以在测试场景中重复进行引用,从而避免了模块功能的重复描述

     

    具体地,我们可以约定将项目的所有模块定义放置在suite目录下,并在suite目录中按照项目的功能来组织模块的定义。
    后续,我们在testcases目录中描述测试场景时,就可同时引用接口定义和模块定义了;模块和接口的混合调用,必将为我们编写测试场景带来极大的灵活性。
    此时测试用例文件的目录结构如下所示:
    ✗ tree tests
    tests
    ├── api # 该文件夹存放所有接口使用的方法
    │ └── v1
    │ ├── Account.yml
    │ ├── BusinessTrip.yml
    │ ├── Common.yml
    │ └── Leave.yml
    ├── common  #  该文件夹存放所有基础通用方法
    ├── data    #  该文件夹存放所有需要的测试数据(对应的导入方法),测试数据生成的脚本
         ├── create_data   # 数据生成器,通过随机数、任意选择等当时生成用例所需的数据
         ├── mysql    # 数据库方法,对于测试过程中需要‘增删改查’数据库的操作
    ├── docs   #  该文件夹存放所有的文件、图片等文件
    ├── reports     #  该文件夹存放自动生成的测试报告
    ├── requirements.txt     ## 该文件记录所有需要用的框架(以便更换环境一键安装)
    ├── debugtalk.py
    ├──.env #  该文件写所有的环境配置数据
    ├── testsuite # 该文件夹存放所有测试模块
    │ ├── BusinessTravelApplication
    │ │ ├── approve - application.yml
    │ │ ├── executive - application.yml
    │ │ ├── reject - application.yml
    │ │ └── submit - application.yml
    │ └── LeaveApplication
    │ ├── approve.yml
    │ ├── cancel.yml
    │ └── submit - application.yml
    └── testcases
    ├── scenario_A.yml
    ├── scenario_B.yml
    └── scenario_C.yml

    需要注意的是,我们在组织测试用例描述的文件目录结构时,遵循约定大于配置的原则
    1、API接口定义必须放置在api目录下
    2、模块定义必须放置在suite目录下
    3、测试场景文件必须放置在testcases目录下
    4、相关的函数定义放置在debugtalk.py中
    至此,我们实现了测试用例的接口 - 模块 - 场景分层,从而彻底避免了重复定义描述。

    另外注意事项:
    1、分层
    该框架分为api(接口)、suite(模块)、test(测试步骤)、config(测试用例)
    api->suite->test->config
    api->test->config
    由于分层结构,所以影响hook机制的使用
    当api中使用hook,则suite、test中将不再可以使用hook(使用也是无效的,运行时不会跑这一块代码)
    当suite使用hook,则test中将不再可以使用hook(使用也是无效的,运行时不会跑这一块代码)
    一个测试用例中,最后一个步骤已经使用hook机制,config处的teardown_hooks基本也是无效的(目前邮件可用)
    所以api处尽量不使用hook。将hook机制用于测试步骤、用例中
    2、参数
    如果在config中使用parameters引入数据,则取值必须时数组形式[{“a”, “b”, “c”}]
    如果在config中使用variables引入数据,则直接取值"a"
    如果在test中使用variables引入数据,则直接取值"a"
    数组类型
    config中使用parameters(全局参数),需要将数据转化为数组类型
    int类型
    test中的json实际使用中,参数为int类型时,需要将数据转化为int类型
    string类型
    config、test中使用variables(全局变量),需要将数据转化为string类型
    python中通过函数传入的非数字,默认string类型
    python中通过函数传入的数字,默认是int类型
    利用函数引用数据
    在debugtalk.py中使用函数取相应的值
    注意:由于上一个脚本获取到的是字符串类型,如果有些参数是整数类型,取值时需要将字符串转化为需要的类型;
    同时注意:如果接口参数中“”传输的字符,不需要转化即使是数字。接口参数中不加“”的类型,使用int()方法转化即可。
    此处的转化主要取绝于测试步骤有没有加”“,另:如果某参数用于url,不需要将其转化类型,字符串类型即可,框架会自动识别
    def get_loan_days():
    return [{"loan_days": int(new_mysql.GetMysqlData(loan_days}]
    在测试用例config部分添加引入,并在具体测试步骤中引用具体数据
    - config:
    parameters:
    - loan_days: ${get_loan_days()}
    具体步骤中引用
    $loan_days
    3、引用
    api、suite引用
    由于引用均使用的是def方法,所以api、suite都是可以使用参数方法的使用场景:某一接口用于两种类型的操作。可以将类型写为参数。在测试步骤中引用xxx(xx)即可
    api中使用参数化方式,某些值选择有函数传入时,注意参数命名一定要与方法中的任何字段不同(并且字段中不能包含参数名)例:api中有1个字段为is_first,参数化时命名绝不能使用is、first
    方法引用
    该框架所有测试步骤、用例中使用的方法均需要再debugtalk文件中声明
    所以方法引用规则:
    具体的方法要单独一个文件来编写
    在debugtalk文件中声明(推荐只做声明,或嵌套参数传出方法)
    4、运行
    由于报告会自动生成,所以需要cd到项目内部,再hrun xxx.yml。这样报告会显示在项目内的报告文件夹中,否则会自动创建一个文件夹存储

    5、样例
    api.yml
    - api:
    def : api_aa_bb_POST()
    name: xxxx
    request:
    headers: {"Content-Type": "application/json"}
    url: $gwhost / xxxx / xxxx / xxxx
    method: POST
    json:
    {
    "phone": "$phone"
    }
    validate:
    - eq: ["ok", true]
    - eq: ["reason", OK]
    suite.yml
    - config:
    def : suite_bb_cc()
    name: xxxx
    - test:
    name: xxxx
    api: api_xxxx_xxxx_POST()

    case.yml
    - config:
    name: xxxxxxxxx
    parameters:
    - phone: ${get_p2g_smoke_main_test_data(phone)}
    - password: ${get_p2g_smoke_main_test_data(password)}
    - test:
    name: xxxxx
    api: api_aa_bb_POST()
    - test:
    name: xxxxx
    suite: suite_bb_cc()
    - test:
    name: xxxxx
    variables:
    - order_no: ${get_approval_applications_from_customer_id_order_no(SMS, $phone)}
    api: api_management_peso2goUpdateAuditingResult()
    setup_hooks:
    - ${get_approval_applications_from_order_no_application_status($order_no)}
    teardown_hooks:
    - ${hook_sleep(60)}
    - ${get_core_loans_from_order_no_repay_status($order_no)}

    (上述部分内容来自:https: // blog.csdn.net / baidu_36943075 / article / details / 96351984
    https: // zhuanlan.zhihu.com / p / 32299255)



  • 相关阅读:
    记一次bash脚本报错原因
    说说JSON和JSONP,也许你会豁然开朗,含jQuery用例(转载)
    python 正则空格xa0实录 与xpath取 div 里面的含多个标签的所有文字
    python3的时间日期处理
    easyui的 一些经验
    hash是什么?
    vue.js 入门
    python __nonzero__方法
    Jmeter之『并发』
    Docker之网络篇
  • 原文地址:https://www.cnblogs.com/mys6/p/14808290.html
Copyright © 2011-2022 走看看