httprunner 使用教程


 
 
#安裝
pip3.7 install httprunner
hrun -V 查看版本,能看到版本信息,就代表安裝成功
hrun -h 查看幫助信息
 
 
#命令說明
在 HttpRunner 安裝成功后,系統中會新增 4 個命令:
httprunner : 核心命令
hrun:httprunner 的縮寫,功能與 httprunner 完全相同
hmake:httprunner make的別名,用於將YAML/JSON測試用例轉換為pytest文件
har2case:輔助工具,可將標准通用的 HAR 格式(HTTP Archive)轉換為YAML/JSON格式的測試用例
 
 
#查看命令說明
httprunner startproject -h
 
 
#新建項目
httprunner startproject httprunner_demo

 

 
#查看項目結構
macname@MacdeMacBook-Pro Desktop % cd httprunner_demo
macname@MacdeMacBook-Pro httprunner_demo %
macname@MacdeMacBook-Pro httprunner_demo % ls -l
total 8
-rw-r--r--@ 1 macname  staff  185 12 10 09:47 debugtalk.py
drwxr-xr-x  2 macname  staff   64 12 10 09:47 har
drwxr-xr-x  2 macname  staff   64 12 10 09:47 reports
drwxr-xr-x  4 macname  staff  128 12 10 09:47 testcases
macname@MacdeMacBook-Pro httprunner_demo %
 

 

#結構說明
debugtalk.py 放置在項目根目錄下(借鑒了pytest的conftest文件的設計)
.env 放置在項目根目錄下,可以用於存放一些環境變量
reports 文件夾:存儲 HTML 測試報告
testcases 用於存放測試用例
har 可以存放錄制導出的.har文件
具體用法會在后續中細講,本章不展開。我們可以點開生成的testcases文件夾下的測試用例,里面是提供了一個可運行的demo內容的,那先來運行一下看看。
 
 
#運行項目
macname@MacdeMacBook-Pro Desktop %
macname@MacdeMacBook-Pro Desktop % hrun httprunner_demo
2020-12-10 09:53:27.334 | INFO     | httprunner.make:__make:512 - make path: /Users/macname/Desktop/httprunner_demo
2020-12-10 09:53:27.340 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2020-12-10 09:53:27.343 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from /Users/macname/Desktop/httprunner_demo/.env
2020-12-10 09:53:27.344 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: USERNAME
2020-12-10 09:53:27.344 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: PASSWORD
2020-12-10 09:53:27.350 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/macname/Desktop/httprunner_demo/testcases/demo_testcase_ref.yml
2020-12-10 09:53:27.361 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2020-12-10 09:53:27.362 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/macname/Desktop/httprunner_demo/testcases/demo_testcase_request.yml
2020-12-10 09:53:27.363 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/macname/Desktop/httprunner_demo/testcases/demo_testcase_request_test.py
2020-12-10 09:53:27.364 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/macname/Desktop/httprunner_demo/testcases/demo_testcase_ref_test.py
2020-12-10 09:53:27.375 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2020-12-10 09:53:27.375 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/macname/Desktop/httprunner_demo/testcases/demo_testcase_request.yml
2020-12-10 09:53:27.376 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
reformatted /Users/macname/Desktop/httprunner_demo/testcases/demo_testcase_ref_test.py
reformatted /Users/macname/Desktop/httprunner_demo/testcases/demo_testcase_request_test.py
All done! ✨ 🍰 ✨
2 files reformatted.
2020-12-10 09:53:27.704 | INFO     | httprunner.cli:main_run:56 - start to run tests with pytest. HttpRunner version: 3.1.4
====================================================== test session starts =======================================================
platform darwin -- Python 3.7.4, pytest-5.4.3, py-1.8.0, pluggy-0.13.0
rootdir: /Users/macname/Desktop
plugins: metadata-1.11.0, html-2.1.1, ordering-0.6, cov-2.8.1
collected 2 items                                                                                                                
 
httprunner_demo/testcases/demo_testcase_request_test.py .                                                                  [ 50%]
httprunner_demo/testcases/demo_testcase_ref_test.py .                                                                      [100%]
 
======================================================= 2 passed in 5.17s ========================================================
macname@MacdeMacBook-Pro Desktop %
 

 

 
#轉換為pytest文件
macname@MacdeMacBook-Pro Desktop % cd httprunner_demo/har
macname@MacdeMacBook-Pro har %
macname@MacdeMacBook-Pro har % har2case yinxiang.har
2020-12-10 10:00:18.639 | INFO     | httprunner.ext.har2case.core:gen_testcase:356 - Start to generate testcase from /Users/macname/Desktop/httprunner_demo/har/yinxiang.har
2020-12-10 10:00:18.639 | INFO     | httprunner.ext.har2case.core:_make_testcase:347 - Extract info from HAR file and prepare for testcase.
2020-12-10 10:00:18.641 | INFO     | httprunner.compat:ensure_testcase_v3:219 - ensure compatibility with testcase format v2
2020-12-10 10:00:18.643 | INFO     | httprunner.loader:load_dot_env_file:127 - Loading environment variables from /Users/macname/Desktop/httprunner_demo/.env
2020-12-10 10:00:18.643 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: USERNAME
2020-12-10 10:00:18.644 | DEBUG    | httprunner.utils:set_os_environ:33 - Set OS environment variable: PASSWORD
2020-12-10 10:00:18.644 | INFO     | httprunner.make:make_testcase:349 - start to make testcase: /Users/macname/Desktop/httprunner_demo/har/yinxiang.har
2020-12-10 10:00:18.645 | INFO     | httprunner.make:make_testcase:442 - generated testcase: /Users/macname/Desktop/httprunner_demo/har/yinxiang_test.py
2020-12-10 10:00:18.646 | INFO     | httprunner.make:format_pytest_with_black:170 - format pytest cases with black ...
reformatted /Users/macname/Desktop/httprunner_demo/har/yinxiang_test.py
All done! ✨ 🍰 ✨
1 file reformatted.
2020-12-10 10:00:19.095 | INFO     | httprunner.ext.har2case.core:gen_testcase:377 - generated testcase: /Users/macname/Desktop/httprunner_demo/har/yinxiang_test.py
Sentry is attempting to send 0 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit
macname@MacdeMacBook-Pro har % ls
__init__.py        yinxiang.har        yinxiang_test.py
macname@MacdeMacBook-Pro har %
macname@MacdeMacBook-Pro har %
 
#轉為YAML:
har2case yinxiang.har -2y
#轉換為JSON:
har2case yinxiang.har -2j

 結果:

macname@MacdeMacBook-Pro har %
macname@MacdeMacBook-Pro har % ls -l
total 216
-rw-r--r--  1 macname  staff     48 12 10 10:00 __init__.py
-rw-r--r--  1 macname  staff  42124 12 10 09:56 yinxiang.har
-rw-r--r--  1 macname  staff  21468 12 10 10:05 yinxiang.json
-rw-r--r--  1 macname  staff  14698 12 10 10:05 yinxiang.yml
-rw-r--r--  1 macname  staff  17863 12 10 10:00 yinxiang_test.py
macname@MacdeMacBook-Pro har %
macname@MacdeMacBook-Pro har %
macname@MacdeMacBook-Pro har %

 

 
 
httprunner可以支持三種格式的用例,分別是pytest、yaml和json。yaml和json是以前的版本所使用的用例格式,但是在3.x版本上,官方強烈建議使用的是pytest格式的用例。
上圖是來自官方的用例格式關系圖,可以看出來,httprunner再對於第三方導出的har文件進行了轉換處理,有的人喜歡轉換成json,有的人喜歡轉換成yaml。但是最終,還是通過解析json格式的文件,生成pytest的python文件。
既然最后都是要生成pytest,那何不一步到位呢?哈哈,我想這就是官方推薦pytest格式的原因吧。
我還是挺喜歡的,因為我對於pytest使用的較多,那么接下來也是基於pytest格式的用例進行解析。
 
 
 
#用例結構分析
錄制生成的case很便捷,但是這並不是說,不需要我們做任何的改動了。在實踐的過程中,我們仍然會根據我們實際項目的不同需求來對case作進一步的調整,所以徹底的了解case的構造尤為重要。
首先,我錄制了一個百度搜索“httprunner”的一個請求,轉換成pytest文件后如下:
可以看到:
  • 每個testcase都是HttpRunner的子類
  • 必須有兩個類屬性:config和teststeps。
  • 單個teststeps列表中的單個Step內部通過鏈式調用(RunRequest().get().with_params().with_header().with_cookies().validate().assert_equal())
  1. config:配置測試用例級設置,包括基礎url、驗證、變量、導出。
  2. teststeps:teststep的列表(list[Step]),每個步驟對應於一個API請求,也可以調用另一個testcase。此外,還支持variables/extract/validate/hooks機制來創建極其復雜的測試場景。
  3. 鏈調用:可以看到一個case的請求,經過了各個環節的調用,這也是httprunner 3.x版本一大亮點。現在的ide編輯器越來越強大,比如你使用pycharm的話,都不用你怎么記憶用例的格式,順手就...(點)出來了,這或許也是官方推薦使用pytest的另一個原因吧,哈哈。
 
 

補一個官方完整的一個demo代碼,並說說httprunner中的用例與我自己編寫的測試用例之間的聯系。
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
 
class TestCaseRequestWithFunctions(HttpRunner):
    config = (
        Config("request methods testcase with functions")
        .variables(
            **{
                "foo1": "config_bar1",
                "foo2": "config_bar2",
                "expect_foo1": "config_bar1",
                "expect_foo2": "config_bar2",
            }
        )
        .base_url("http://demo.qa.com")
        .verify(False)
        .export(*["foo3"])
    )
 
    teststeps = [
        Step(
            RunRequest("get with params")
            .with_variables(
                **{"foo1": "bar11", "foo2": "bar21", "sum_v": "${sum_two(1, 2)}"}
            )
            .get("/get")
            .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
            .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"})
            .extract()
            .with_jmespath("body.args.foo2", "foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.args.foo1", "bar11")
            .assert_equal("body.args.sum_v", "3")
            .assert_equal("body.args.foo2", "bar21")
        ),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo2": "bar23"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "$expect_foo1")
            .assert_equal("body.form.foo2", "bar23")
            .assert_equal("body.form.foo3", "bar21")
        ),
    ]
 
 
if __name__ == "__main__":
    TestCaseRequestWithFunctions().test_start()
 

#


#
  1. httprunner中的testcase,其實說的就是上面的這一整個Python文件。
  2. teststeps列表中的Step,其實就是我自己編寫case時候的一個個def test_xxx():pass。
  3. 而每一個Step內部,依然是按照 傳參——調用接口——斷言,這樣的過程來的。
萬變不離其宗,httprunner框架目前看起來,確實可以讓編寫更加的便捷、簡潔,但是這只是目前從demo的過程中得到的結論,后面還需要落地實戰才可以。
我們了解到了config,在配置中,我們可以配置測試用例級級別的一些設置,比如基礎url、驗證、變量、導出。
我們一起來看,官方給出的一個例子:
 
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
 
 
class TestCaseRequestWithFunctions(HttpRunner):
    config = (
        Config("request methods testcase with functions")
        .variables(
            **{
                "foo1": "config_bar1",
                "foo2": "config_bar2",
                "expect_foo1": "config_bar1",
                "expect_foo2": "config_bar2",
            }
        )
        .base_url("http://demo.qa.com")
        .verify(False)
        .export(*["foo3"])
    )
 
    teststeps = [
        Step(
            RunRequest("get with params")
            .with_variables(
                **{"foo1": "bar11", "foo2": "bar21", "sum_v": "${sum_two(1, 2)}"}
            )
            .get("/get")
            .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
            .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"})
            .extract()
            .with_jmespath("body.args.foo2", "foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.args.foo1", "bar11")
            .assert_equal("body.args.sum_v", "3")
            .assert_equal("body.args.foo2", "bar21")
        ),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo2": "bar23"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "$expect_foo1")
            .assert_equal("body.form.foo2", "bar23")
            .assert_equal("body.form.foo3", "bar21")
        ),
    ]
 
 
if __name__ == "__main__":
    TestCaseRequestWithFunctions().test_start()
 

 

一、name(必填)

即用例名稱,這是一個必填參數。測試用例名稱,將顯示在執行日志和測試報告中。比如,我在之前的百度搜索的case里,加入name。

二、base_url(選填)

其實這個配置一般在多環境切換中最常用。
比如你的這套測試用例在qa環境,uat環境都要使用,那么就可以把基礎地址(舉例 http://demo.qa.com),設置進去。在后面的teststep中,只需要填上接口的相對路徑就好了(舉例 /get)。
這樣的話,切換環境運行,只需要修改base_url即可。

三、variables(選填)

變量,這里可以存放一些公共的變量,可以在測試用例里引用。這里大家可以記住這個“公共”的詞眼,因為在后面的Step中,還會有步驟變量。
比如說,我的接口有個傳參是不變的,比如用戶名username,而且后面的沒個Step都會用到這個傳參,那么username就可以放在config的公共變量里。
另外,Step里的變量優先級是比config里的變量要高的,如果有2個同名的變量的話,那么引用的時候,是優先引用步驟里的變量的。

四、verify(選填)

用來決定是否驗證服務器TLS證書的開關。
通常設置為False,當請求https請求時,就會跳過驗證。如果你運行時候發現拋錯SSLError,可以檢查一下是不是verify沒傳,或者設置了True。

五、export(選填)

導出的變量,主要是用於Step之間參數的傳遞。還是以上面的官方代碼為例:
  1. 在config中配置export“foo3”這個變量。
  2. 在第一個Step中,.extract() 提取了"body.args.foo2"給變量“foo3”。
  3. 在第二個Step中,引用變量"foo3"。
像參數傳遞,提取這些點,會放在后面單獨講解,前面還是以熟悉框架為主。
 
 

測試用例分層模型

一個testcase里(就是一個pytest格式的Python文件)可以有一個或者多個測試步驟,就是teststeps[]列表里的Step。
我的理解每一個Step就可以類比成pytest框架下的def test_xxx()的用例函數,在Step里通常都會要請求API完成測試,也可以調用其他測試用例來完成更多的需求。
可以來看下官方的測試用例邏輯圖(2.x版本不同,3.x棄用了2.x的API概念):
 
可以看到,testsuite包含了testcase,testcase1需要依賴testcase2才可以完成,那么就可以在teststep12對其進行引用;而testcase2又依賴於testcase3,那么也可以在teststep22進行引用。
但是在整個testsuite下,這3個testcase都是相互獨立的,可以獨自運行。如果需要相互調用,則是在testcase內部去完成處理。
可能看起來有點繞,其實官方想表達的就是測試用例分層的一個思想:
  • 測試用例(testcase)應該是完整且獨立的,每條測試用例應該是都可以獨立運行的
  • 測試用例是測試步驟(teststep)的有序集合
  • 測試用例集(testsuite)是測試用例的無序集合,集合中的測試用例應該都是相互獨立,不存在先后依賴關系的;如果確實存在先后依賴關系,那就需要在測試用例中完成依賴的處理
其實這一點,在我們自己使用pytest框架編寫測試用例的時候同樣貫徹到了。為了自動化測試的穩定性和可維護性,每個測試用例之間相互獨立是非常有必要的。
 
1.RunRequest(name)
RunRequest的參數名用於指定teststep名稱,它將顯示在執行日志和測試報告中。
2. .with_variables
用於存放變量,但是這里的是Step步驟里的變量,不同的Step的變量是相互獨立的。所以對於多個Step都要使用的變量,我們可以放到config的變量里去。
另外,如果config和Step里有重名的變量,那么當你引用這個變量的時候,Step變量會覆蓋config變量。
3. .method(url)
這里就是指定請求API的方法了,常用的get、post等等。如圖所示,就是用的get方法,括號里的url就是要請求的地址了。
這里要注意的是,如果在config里設置了基礎url,那么步驟里的url就只能設置相對路徑了。
4. .with_params
這個就簡單了,測接口不都得要傳參么,對於params類型的傳參,就放這就行了,key-value鍵值對的形式。對於body形式的傳參,看后面。
5. .with_headers
同樣,有header要帶上的就放這里。
6. .with_cookies
需要帶cookie的,可以用.with_cookies方法。
7. .with_data
對於body類型的傳參,可以用.with_data。
8. .with_json
如果是json類型的body請求體,可以用.with_json。
9. .extract
這里就是要做提取操作了,使用.with_jmespath(jmes_path: Text, var_name: Text)。
這里是采用了JMESPath語言,JMESPath是JSON的查詢語言,可以便捷的提取json中你需要的元素。
第一個參數是你的目標元素的jmespath表達式,第二個元素則是用來存放這個元素的變量,供有需要的引用。
這里不展開,后面單講。
10. .validate
斷言,我們測試最終就是要驗證接口返回是否符合預期。
那在httprunner框架中,可以使用assert_XXX(jmes_path: Text, expected_value: Any)來進行提取和驗證。
第一個參數還是jmespath表達式,第二個參數則是預期值。
assert_XXX這種方式相信用過自動化測試框架的都不會陌生,所以也非常容易上手。目前httprunner還是封裝了非常豐富的斷言方法的,相信可以滿足絕大多數的需求了。
 
teststeps = [
        Step(
            RunTestCase("request with functions")
            .with_variables(
                **{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}
            )
            .call(RequestWithFunctions)
            .export(*["foo3"])
        ),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo1": "bar1"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "bar1")
            .assert_equal("body.form.foo2", "bar21")
        ),
]
1. RunTestCase(name)
這個參數呢還是一個名稱,畢竟RunTestCase還是一個Step,這個名稱同樣會在日志和報告中顯示。
2. .with_variables
這個變量跟RunRequest里的用法一樣。
3. .call
這里就是指定你要引用的testcase類名稱了。
4. .export
可以指定要導出的變量,以供后續Step引用。
可以看的.export()內部是一個列表[],這里可以用來導出多個變量。
 
 
一、運行testcase的幾種場景
1. 運行單個case
通常單個case的話我會在編輯器里用main方法運行,不過也可以用命令行運行,看你喜歡。
main方法里在類的后面調用test_start()方法即可。
命令行的話,就是直接在hrun后面加上case的路徑,就可以運行了。
hrun httprunner_demo\testcases\get_user_name_test.py
 
from httprunner import HttpRunner, Config, Step, RunRequest
 
class TestCaseRequestWithGetUserName(HttpRunner):
    config = (
        Config("test /getUserName")
            .base_url("http://localhost:5000")
            .verify(False)
            .export(*["username"])
 
    )
    teststeps = [
        Step(
            RunRequest("getUserName")
                .get("/getUserName")
                .extract()
                .with_jmespath("body.username", "username")
                .validate()
                .assert_equal("body.username", "wesson")
        ),
 
    ]
 
if __name__ == "__main__":
    TestCaseRequestWithGetUserName().test_start()#這里
 

 

2. 運行多個case
hrun httprunner_demo\testcases\get_user_name_test.py  httprunner_demo\testcases\join_str_test.py

 

3. 運行整個case文件夾
hrun httprunner_demo\testcases\

 

二、運行YAML/JSON文件格式的case
當你轉換har文件時候,同級文件夾下,就會生成對應的pytest的文件,文件名稱的末尾會有_test。
如果運行YAML/JSON文件,其實httprunner會先把它們轉換為pytest格式的,再去運行。
所以,用httprunner 3.x版本的話,寫case的話還是直接用pytest格式吧。
 
 
三、運行pytest格式的文件
對應pytest文件來說,用hrun或者pytest命令運行都是可以的。
因為hrun只是封裝了pytest,所以pytest運行的所有參數,同樣可以在hrun后面加。
hrun -h
pytest -h
 
 
用例引用
#get_user_name_test.py
from httprunner import HttpRunner, Config, Step, RunRequest
 
 
class TestCaseRequestWithGetUserName(HttpRunner):
    config = (
        Config("test /getUserName")
            .base_url("http://localhost:5000")
            .verify(False)
            .export(*["username"])#這里定義出要導出的變量
 
    )
 
    teststeps = [
        Step(
            RunRequest("getUserName")
                .get("/getUserName")
                .extract()
                .with_jmespath("body.username", "username")#提取出目標值,賦值給username變量
                .validate()
                .assert_equal("body.username", "wesson")
        ),
 
    ]
 
 
if __name__ == "__main__":
    TestCaseRequestWithGetUserName().test_start()
 
#TestCaseRequestWithJoinStr.py
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
from .get_user_name_test import TestCaseRequestWithGetUserName #記得要導入引用的類
 
 
class TestCaseRequestWithJoinStr(HttpRunner):
    config = (
        Config("test /joinStr")
            .base_url("http://localhost:5000")
            .verify(False)
    )
 
    teststeps = [
        Step(
            RunTestCase("setUp getUserName")
            .call(TestCaseRequestWithGetUserName)#導入后就可以調用了
            .export(*["username"])#在RunTestCase步驟中定義這個變量的導出
        ),
        Step(
            RunRequest("joinStr")
                .get("/joinStr")
                .with_params(**{"str1": "hello", "str2": "$username"})#在第二個傳參中引用導出的變量
                .validate()
                .assert_equal("body.result", "hello $username")#斷言的預期值也引用變量
        ),
 
    ]
 
 
if __name__ == "__main__":
    TestCaseRequestWithJoinStr().test_start()
 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM