在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
目錄中,包含了一份完整的分層測試用例示例,相信會對你們有所幫助。