一、测试用例分层目的
解决测试场景增多,导致接口定义描述的维护困难和繁琐问题。
例如,某个项目中存在三个测试场景(引用官网的示例)
场景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)