在HttpRunner
中,测试用例引擎最大的特点就是支持YAML/JSON
格式的用例描述形式。git
采用YAML/JSON
格式编写维护测试用例,优点仍是很明显的:github
以最多见的登陆注销为例,咱们的测试用例一般会描述为以下形式:django
- config:
name: demo-login-logoff
variable_binds:
- UserName: test001
- Password: 123456
request:
base_url: http://xxx.debugtalk.com
headers:
Accept: application/json
User-Agent: iOS/10.3
- test:
name: Login
request:
url: /api/v1/Account/Login
method: POST
json:
UserName: $UserName
Pwd: $Password
VerCode: ""
validators:
- eq: ["status_code", 200]
- eq: ["content.IsSuccess", True]
- eq: ["content.Code", 200]
- test:
name: Logoff
request:
url: /api/v1/Account/LoginOff
method: GET
validators:
- eq: ["status_code", 200]
- eq: ["content.IsSuccess", True]
- eq: ["content.Code", 200]
复制代码
相信你们已经对该种用例描述形式十分熟悉了。不过,该种描述形式的问题在于,接口一般会出如今多个测试场景中,而每次都须要对接口进行定义描述,包括请求的URL、Header、Body、以及预期响应值等,这就会产生大量的重复。编程
例如,在某个项目中存在三个测试场景:json
API_1/2
)、登陆新注册的帐号(API_3/4/5
)、查看登陆状态(API_6
);API_3/4/5
)、注销登陆(API_7/8
);API_7/8
)、查看登陆状态(API_6
)、注册新帐号(API_1/2
)。按照常规的接口测试用例编写方式,咱们须要建立3个场景文件,而后在各个文件中分别描述三个测试场景相关的接口信息。示意图以下所示。api
在本例中,接口(API_1/2/6
)在场景A和场景C中都进行了定义;接口(API_3/4/5
)在场景A和场景B中都进行了定义;接口(API_7/8
)在场景B和场景C中都进行了定义。能够预见,当测试场景增多之后,接口定义描述的维护就会变得很是困难和繁琐。bash
那要如何进行优化呢?app
其实也很简单,在编程语言中,若是出现重复代码块,咱们一般会将其封装为类或方法,而后在须要时进行调用,以此来消除重复。一样地,咱们也能够将项目的API进行统必定义,里面包含API的请求和预期响应描述,而后在测试场景中进行引用便可。编程语言
示意图以下所示。模块化
具体地,咱们能够约定将项目的全部API接口定义放置在api
目录下,并在api
目录中按照项目的系统模块来组织接口的定义;同时,将测试场景放置到testcases
目录中。
此时测试用例文件的目录结构以下所示:
✗ tree tests
tests
├── api
│ └── v1
│ ├── Account.yml
│ ├── BusinessTrip.yml
│ ├── Common.yml
│ └── Leave.yml
├── debugtalk.py
└── testcases
├── scenario_A.yml
├── scenario_B.yml
└── scenario_C.yml
复制代码
而对于API接口的定义,与以前的描述方式基本一致,只作了两点调整:
block
)的标识为api
;def
字段,形式为api_name(*args)
,做为接口的惟一标识ID;须要注意的是,即便api
没有参数,也须要带上括号,api_name()
;这和编程语言中定义函数是同样的。- api:
def: api_v1_Account_Login_POST($UserName, $Password)
request:
url: /api/v1/Account/Login
method: POST
json:
UserName: $UserName
Pwd: $Password
VerCode: ""
validators:
- eq: ["status_code", 200]
- eq: ["content.IsSuccess", True]
- eq: ["content.Code", 200]
- api:
def: api_v1_Account_LoginOff_GET()
request:
url: /api/v1/Account/LoginOff
method: GET
validators:
- eq: ["status_code", 200]
- eq: ["content.IsSuccess", True]
- eq: ["content.Code", 200]
复制代码
有了接口的定义描述后,咱们编写测试场景时就能够直接引用接口定义了。
一样是背景描述中的登陆注销场景,测试用例就描述为变为以下形式。
- config:
name: demo
variable_binds:
- UserName: test001
- Password: 123456
request:
base_url: http://xxx.debugtalk.com
headers:
Accept: application/json
User-Agent: iOS/10.3
- test:
name: Login
api: api_v1_Account_Login_POST($UserName, $Password)
- test:
name: Logoff
api: api_v1_Account_LoginOff_GET()
复制代码
不难看出,对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
├── debugtalk.py
├── suite
│ ├── 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
复制代码
须要注意的是,咱们在组织测试用例描述的文件目录结构时,遵循约定大于配置的原则:
api
目录下suite
目录下testcases
目录下debugtalk.py
中至此,咱们实现了测试用例的接口-模块-场景
分层,从而完全避免了重复定义描述。
得益于约定大于配置的原则,在HttpRunner
中实现了一个脚手架工具,能够快速建立项目的目录结构。该想法来源于Django
的django-admin.py startproject project_name
。
使用方式也与Django
相似,只须要经过--startproject
指定新项目的名称便可。
$ hrun --startproject helloworld
INFO:root: Start to create new project: /Users/Leo/MyProjects/helloworld
INFO:root: created folder: /Users/Leo/MyProjects/helloworld
INFO:root: created folder: /Users/Leo/MyProjects/helloworld/tests
INFO:root: created folder: /Users/Leo/MyProjects/helloworld/tests/api
INFO:root: created folder: /Users/Leo/MyProjects/helloworld/tests/suite
INFO:root: created folder: /Users/Leo/MyProjects/helloworld/tests/testcases
INFO:root: created file: /Users/Leo/MyProjects/helloworld/tests/debugtalk.py
复制代码
运行以后,就会在指定的目录中生成新项目的目录结构,接下来,咱们就能够按照测试用例的接口-模块-场景
分层原则往里面添加用例描述信息了。
若是看到这里你还不明白测试用例分层的必要性,那也不要紧,测试用例分层不是必须的,你仍是能够按照以前的方式组织测试用例。不过当你某一天发现须要进行分层管理时,你会发现它就在那里,很实用。
最后,在HttpRunner
项目的examples/HelloWorld
目录中,包含了一份完整的分层测试用例示例,相信会对你们有所帮助。