Httprunner:首先介紹下httprunner是干嘛的???,官方文檔是這樣描述的HttpRunner 是一款面向 HTTP(S) 協議的通用測試框架,只需編寫維護一份 YAML/JSON 腳本,即可實現自動化測試、性能測試、線上監控、持續集成等多種測試需求。。。。看到了吧這個框架的牛逼之處 官方文檔鏈接:https://cn.httprunner.org/
它的核心特性是這樣的:
繼承 Requests 的全部特性,輕松實現 HTTP(S) 的各種測試需求
采用 YAML/JSON 的形式描述測試場景,保障測試用例描述的統一性和可維護性
借助輔助函數(debugtalk.py),在測試腳本中輕松實現復雜的動態計算邏輯
支持完善的測試用例分層機制,充分實現測試用例的復用
測試前后支持完善的 hook 機制
響應結果支持豐富的校驗機制
基於 HAR 實現接口錄制和用例生成功能(har2case)
結合 Locust 框架,無需額外的工作即可實現分布式性能測試
執行方式采用 CLI 調用,可與 Jenkins 等持續集成工具完美結合
測試結果統計報告簡潔清晰,附帶詳盡統計信息和日志記錄
極強的可擴展性,輕松實現二次開發和 Web 平台化
特點:
擴展性高,測試用例是一份json/yaml格式的文件,不用自己錄接口參數,用har2case命令將抓到har導出成一份json/yaml格式的測試用例文件。也就是說fiddler抓到接口導出成har包,用命令執行一下,測試用例就錄制好了,如果測試用例沒有依賴的情況出現,就能用命令運行。能寫python代碼,能調python各種,有能力的將httprunner自帶的命令進行封裝。完美吧!!!
注意:以下全部只針對我的項目 項目代碼下載鏈接:https://files.cnblogs.com/files/wenjxu/AutoMateProject.rar
1:官方文檔只有錄制testcase的功能,我這邊參照har2case的源碼重新寫了一遍,附加一個錄制api的功能, 命令如下:python spell.py -a demo.har,執行后會在api文件路徑生成一個demo.yaml文件
2:官方生成testcase的命令是har2case demo.har,我這邊重新了寫了一個命令python spell.py -t demo.har
開始上手:
准備:python3.7(自帶pip),ide(pycharm) 這兩個最常規的需要
安裝環境:
步驟1
步驟2
步驟3
步驟4
步驟5
最后點擊OK,OK,OK就可以了
也就是說安裝一個python3.7和一個pycharm,並且在setting-》Project Interpreter 添加你下載的python3 文件下python.exe可執行程序就可以了
安裝第三方依賴包:
1:打開Terminal輸入命令pip install pipenv安裝pipenv包管理工具,這個工具也是一個虛擬環境(非常好用)
2:輸入命令pipenv install,會根據pipfile文件中的字段[packages]來下載對應的依賴包
3:下載好了后去找虛擬環境,把它加載到setting配置中,下載的虛擬環境其實就是一堆文件,一般會在c盤個人用戶路徑下。
備注:這三個依賴包就下載好了,之所以搞虛擬環境pipenv,是因為假如你寫好代碼下載了很多第三方依賴包也用到了,假如別人要用你的代碼的時候並不知道要下載哪些第三方依賴包,還得自己一個一個文件去找麻煩的很,先利用pip install pipenv命令下載pipenv這個工具,然后pipenv install 一個命令就能下載所有需要的依賴包,這個命令會根據Pipfile文件中的[packages]來下載對應的包,以后寫個代碼需要用到第三方包的話,也可以pipenv install packagename 一下,非常方便,移植性也很高
開始工作
1:打開Fiddler抓取一個接口(最好沒有接口依賴關系)
2:選中該接口,點擊file-》Export Sessions -》Selected Sessions-》Next
3:導出har,最好存放的位置是在自己項目里,方便查找。
4:打開Terminal,輸入命令python spell.py -a demo.har,這個執行完成后會在api目錄生成一個以url 路徑為名的yaml文件,不要刪除demo.har后面生成testcase測試用例會用到。
5:打開文件這個yaml文件預覽
5.1:這個文件總共有name,base_url,variables,request,validate字段
name:api接口描述
base_url:接口的域名
variables:變量參數,用於其它地方可以引用,引用方式$變量名
request:請求url,參數,method等等
validate:校驗的請求返回的值
6:生成testcase測試用例,輸入命令python spell.py -t demo.har,這個命令執行完成后,會在testcases 目錄下生成一個以har文件名的json文件
7:打開該json文件預覽,這個json里的數據是一個list of dict格式
7.1 第一個dict是config配置,name是testcase描述,variables(list of dict)里的變量是用於其它地方可以引用,作用域是整個文件,引用方式$變量名,output(list)是用於打印變量
7.2 第二個dict是一個teststep,如果有第三個dict的話,那也是一個teststep。
7.3 第二個dict中test是必須的,每一個teststep都會有,name是這個測試步驟的描述,api對應是一個目錄路徑(引用api目錄中yaml文件),variables(list of dict)同樣里面的變量用於其它地方引用,但是作用域只是這個teststep。extract(list of dict)用於處理依賴,validate用於校驗請求返回的值,setup_hooks和teardown_hooks用於請求前的准備工作和請求后的結尾工作,先簡單的描述下
8:輸入命令hrun testcases\demo.json,執行完成后會在reports目錄生成一個html文件
注意:/是linux中的斜桿,\是windows的斜桿
點擊log-1
這個報告描述的很詳細,非常的完成,可以慢慢體會下。
怎么理解這些生成文件的相互依賴關系和參數的調用和引用。也就是說我們日常如何去維護這些文件
一、理解api目錄下yaml文件:
首先聲明:一個yaml文件對應一個api接口,也就是說api目錄下yaml文件內容只能是一個接口
1:讓我們看下api目錄下yaml文件,在用命令python spell.py -a demo.py(我自己參照源碼寫的一個錄制api文件的功能)生成文件的時候,我將har文件中有關接口的參數數據都添加到variables(list of dict)中,其實它是這樣的格式 [{“name”:”wenjxu”},{“from”:”3”}],而request: data | request: params | request: json都是在$引用variables的變量,為啥這樣干呢?是因為testcases目錄中json文件引用api目錄下yaml文件僅僅覆蓋其variables值,就能在json文件中引用多次該接口,讓整個json文件看起來簡潔不會存在很多重復的參數。(后續還會講到)
2:request:data | request:params | request:json 這些請求的參數為啥分三種類型,其實跟接口請求有關系
params這種參數一般是url路徑提交參數一種方式,一般是跟在url?后面,例如url?from=3這種形式
data這種參數需要分區請求頭content-type三種類型了(application/x-www-form-urlencoded,multipart/form-data,application/json),如果是content-type是form-data和urlencoded,那么請求的參數存放在data中,如果是content-type是application/json,那么請求的參數存放在json中。(其實不需要太在意這些放在哪里。因為官方文檔生成testcase也是這樣做的,其實我們只需要關注有哪些參數就行)
3:然后看下validate(list of dict)其實它的格式是這樣的,例如:[{“eq”:[“status_code”,200] }],這種說明校驗這個接口運行后的狀態碼是否是200,我們可以這樣[{“eq”:[“content.code”,”0”] }] ,這種說明我們需要校驗接口返回結果的code是否是”0”,content關鍵字表示的是接口返回的值可以想象成一個變量,這種寫法是固定的。不光可以eq比較值還有其它校驗的方法
1、實際結果和期望結果是否相等:equals 或 eq 或 == 或 is
2、實際結果小於期望結果:less_than 或 lt
3、實際結果小於等於期望結果:less_than_or_equals 或 le
4、實際結果大於期望結果:greater_than 或 gt
5、實際結果大於等於期望結果:greater_than_or_equals 或 ge
6、實際結果和期望結果不相等:not_equals 或 ne
7、實際結果和期望結果是否相等:string_equals 或 str_eq
8、實際結果的長度和期望結果是否相等:length_equals 或 len_eq 或 count_eq
9、實際結果的長度大於和期望結果:length_greater_than 或 len_gt 或 count_gt 或 count_greater_than
10、實際結果的長度大於等於期望結果:length_greater_than_or_equals 或 len_ge 或 count_ge 或 count_greater_than_or_equals
11、實際結果的長度小於期望結果:length_less_than 或 len_lt 或 count_lt 或 count_less_than
12、實際結果的長度小於等於期望結果:length_less_than_or_equals 或 len_le 或 count_le 或 count_less_than_or_equals
13、實際結果包含期望結果:contains
14、實際結果被期望結果包含:contained_by
15、實際結果的字段類型和期望結果相同:type_match
16、檢查實際結果和期望結果是否都是basestring的實例,如果都是就用正則匹配兩個輸入參數:regex_match
17、驗證check_value是否已expect_value開始:startswith
18、驗證check_value是否已expect_value結尾:endswith
二、理解testcases目錄下json文件
1: 讓我們來看下testcases下的json文件,config配置的作用域是全局的,也就是說里邊的variables里的變量能讓多個teststep引用,重說一遍第二個dict或者第三個dict都是一個teststep,里面的關鍵字幾乎都是一樣的。name是teststep的描述,api關鍵字(遵循官方文檔測試用例分層機制)是引用api目錄下對應的yaml文件,teststep中variables的作用域只是在當前teststep中有效。有一個特別注意的地方是如果teststep中variables有一個變量與config中variables重復,會優先使用teststep中變量,如果說config配置variables中變量與api目錄下yaml文件中variables一樣的話,會優先引用config配置中,總結一句話“就近原則”,validate也是一樣的道理
2: 繼續看json文件居然沒有請求的信息????,請求的信息從哪里來,讓我們打開api目錄下yaml文件,請求的信息其實就是從api目錄下yaml文件拿到request的相關信息。如果json文件中定義了request相關信息,還是那就話“就近原則”,不過我們不需要在json文件中定義request相關信息,如果每個teststep都定義了request信息的話就會存在大量參數看起來像是重復了,我們只需要添加需要變的參數到teststep中variables進行覆蓋。是不是感覺有點像繼承,子類(teststep)沒有的話去找父類(config),父類沒有找父父類(api目錄下yaml文件),以此類推,子類有的話就用子類的。
三、處理復雜場景
1:參數依賴
1.1:想象一種場景,假如我們有一個接口是需要登陸接口請求后返回的token值,也就是說很多時候,接口之間有很多是相互關聯,必須執行A接口拿到請求返回的值,將A接口請求返回的值拿到然后傳入B接口中(當成B接口的請求參數)。
1.2:我們正確的做法是使用extract(list of dict)關鍵字,它的格式是這樣的例如:[ {“token”:” content.token” } ],token是一個被其它接口所依賴的變量自己命名能看的懂就行,content.token 表示接口返回值.key,這是一個固定格式以點key式取值,如果接口的返回值是{“info”:[“name”,” age”],”msg”}這樣的話,我們想引用[ ]里name字符串的話,格式是content.info[0],也就是說如果是list就用[index]引用,如果是dict就是點key進行引用。當我們extract 提取到接口返回的值的時候, 我們就可以$token引用傳入下一個teststep中(建議放到下一個teststep的variables中,可以進行覆蓋),說明一下extract提取的token是保存到內存當中並且這個值能被多次引用,並且不用擔心引用多次造成所依賴的接口運行多次的問題(因為自始至終只會運行一次所依賴的接口)
2:調用函數
個人覺得這個框架擴展性最高的地方就是調用函數了,調用sql,執行sql都需要用到調用函數,像 setup_hook和teardown_hook兩個方法都是需要調用函數(后續會講到),例如我們需要一個時間,上傳一個文件,拿一個隨機字符串都是可以通過調用函數解決,調用方式:"${gen_random_string(15)}" "${函數名(參數值,參數值)}",注意調用的函數都是寫在debugtalk文件中,參數值例如一個字符串千萬中間別有空格要不然會報錯,如果字符串中間有空格的話例如sql語句,我們可以將sql語句寫到variables中用一個變量保存然后調用函數的時候就可以這樣寫"${gen_random_string($v,$r)}"
3:參數化
1.1參數化驅動其實就是提前准備一堆參數,然后循環調用同一個測試用例執行,另外重申一遍我們最好將 接口請求的參數抽取到variables中,方便我們針對某一個變量進行覆蓋,開展很多工作也很方便。一般我們在testsuites目錄下建json或者yaml文件開展參數化工作。我一般喜歡用json文件,只有testapi目 錄才會使用到yaml文件。
1.2 模板如下
{
"config": {
"name": "create users with parameters"
},
"testcases": {
"create user $user_id": {
"testcase": "demo-quickstart.json",
"parameters": {
"user_agent": ["iOS/10.1", "iOS/10.2", "iOS/10.3"]
}
}
}
}
parameters其它調用方式:
"user_id": "${P(data/account.csv)}",
"username-password": "${get_account(10)}"
關鍵字parameters里添加需要驅動的參數名,對應一個列表多個參數。demo-quickstart.json文件這個 總共有三種書寫參數值方式(1:對應一個列表 2:引用csv文件 3:調用函數),這里只講第一種方式, 重復申明一遍我們最好把testcase里接口請求的參數都寫在自己的variables中(不是config中), 方便 覆蓋開展工作。注意如果demo-quickstart.json文件中有兩個接口其中一個接口會用到user_agent參 數,運行這個參數化文件會總共運行3次,總共運行6個api接口(包括重復的),3次其中一個接口會 使用上不同的參數。
關於參數化數據驅動,這里只描述了最簡單的場景和使用方式,如需了解更多,請進一步閱讀官方文檔 https://cn.httprunner.org/prepare/parameters/
4:setup_hook測試前的准備工作,teardown_hook測試后的收尾工作
1: 這兩個方法太有用了,有些場景我們錄制好了測試用例運行一次就結束了,再想運行的話就會報錯。例如我們的項目簽署協議一塊,我們簽署一次就結束了,一般開發邏輯可能是簽署成功后把某個字段置為例如1,我們想再次運行這個測試用例就必須提前把這個字段update置為0,其實就是在運行測試用例之前我們調用執行下sql,setup_hook或者 teardown_hook就是用來專門干這種事的,其意思是每個 teststep之前觸發setup_hook函數(用於准備工作),每個teststep之后觸發teardown_hook函數(用於測試后的清理工作),目前我能想象到就這個場景。
2: 再看testcase中json文件,teardown_hook和setup_hook兩個都是一個列表[],里面可以存多個字符串,注意這些字符串一般是”${函數名(參數)}”可以寫多個這種,運行的時候會依次調用,注意如果寫其它的字符串的話就失去了其意義。
5:cookies依賴處理
1:重點來了,cookies依賴這個是一個比較棘手的問題,我們模擬平時工作的場景,例如登陸一個系統, 我們只是登陸一次,服務器返回cookies並且存到客戶端中,其它接口請求都是從客戶端拿到並且攜帶一塊去服務器請求,注意登陸獲取cookies的接口只運行一次該如何處理???
會寫代碼可以看看:
我目前能想到這樣處理,框架有個方法https://cn.httprunner.org/development/dev-api/
runner實例化出的對象可以用於執行一個api文件或者json文件,執行文件完成后這個對象有個屬性應該是_summary(官方文檔沒有更新)是運行結果數據(具體數據結構看下運行出來結果,官方文檔有些沒更新),我們只要取到 cookies並且利用RequestsCookieJar模塊設置完成后返回一個對象(api文件可以調用)。有一個問題,我們說會有多接口都會依賴cookies,每個測試用例都會調用,那豈不是執行一個測試用例都是會執行下登陸接口,不符合我們的預期,該如何做???我們可以這樣將設置好的cookies對象保存到緩存中,如果緩存中有了就取緩存中的cookies,如果沒有就執行登陸接口,自始至終只會運行 一遍登陸接口,具體代碼實現省略。。。。
用法。。。。。。
2:先准備錄制好登陸接口,在api文件的variables中調用debugtalk.py文件中get_cookies函數(傳入登陸接口文件的路徑) ,賦予一個變量cookie_jar(可以賦予,可以不賦予直接調用),然后在api文件request中加上cookies字段引用上面的cookie_jar變量,注意這個函數比較特殊一定要傳入能拿到cookies接口文件,其它文件就報錯了, 用法比較嚴格
6:文件上傳
1:文件上傳一般都是基於form-data這種請求頭進行處理,這個格式真的非常特殊非常特殊非常特殊,格式如圖:
如果是普通的請求參數,格式是這樣的:name=”請求參數名”,緊接着參數值是在\r\n\r\n“值”\r\n 里面。如果是有關文件的請求參數,格式是這樣的:name=”請求參數名”; filename結尾,緊接着參數值是在\r\n\r\n”值”\r\n里面,但是值一個二進制數據。
2:說明一下,httprunner框架針對這個form-data格式的接口,我覺得有點沒有處理好。Httprunner框架是將這些亂七八糟的數據都序列化了你用hrun命令執行一下就會看到一長串的數據,我是這樣處理的, 如果請求頭是form-data格式的,就用正則分兩步匹配出相關的請求參數和請求值,第一步匹配普通的請求參數和值,第二部匹配文件相關的請求參數但是不匹配值,而是一個格式"${get_file(file_path)}"(后續會說這個為啥寫這個格式),整個接口如果有文件相關的請求參數就匹配,如果沒有就不匹配。
3:把文件相關的請求參數和普通的請求參數都提前出來就好處理了,如果有文件相關的請求參數,那么在生成yaml格式的api文件的時候,就寫入文件中,注意headers把content-type等於form-data相關的去掉,我寫的功能python spell -a demo.har 在生成api文件的時候就已經忽略了(要不然運行文件會出錯)
這個文件傳參格式也是固定的。這個格式是requests請求的一個傳參格式,如圖
files(固定語法)字段專門用來上傳文件,對一個字典格式,img上傳文件請求的參數名,open(“文件路 徑”, “rb”)是一個以二進制讀的方式打開一個文件,返回一個對象(文件句柄)。如果有多個上傳文件請求的參數的話也會將其存到該字典當中。注意看我們生成的yaml文件file:是調用一個函數,也就是說我們必須在debugtalk.py文件中寫入一個函數如圖
平常用的時候,只需要注意是否有文件上傳的字段,如果有只需要把相關文件放在項目的data路徑下,然后將file_path覆蓋成一個文件的路徑就可以了運行了