• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • HttpRunner 實現參數化數據驅動機制

    發表于:2018-02-26來源:debugtalk作者:debugtalk點擊數: 標簽:HttpRunner
    要造一個輪子,最好是先看下現有知名輪子的實現機制。之前有用過一段時間的 LoadRunner,對其參數化機制印象蠻深的,雖然它是性能測試工具,但在腳本參數化方面是通用的。

    背景

    自動化測試中,經常會遇到如下場景:

    1、測試搜索功能,只有一個搜索輸入框,但有10種不同類型的搜索關鍵字;
    2、測試賬號登錄功能,需要輸入用戶名和密碼,按照等價類劃分后有20種組合情況。

    這里只是隨意找了兩個典型的例子,相信大家都有遇到過很多類似的場景??偨Y下來,就是在我們的自動化測試腳本中存在參數,并且我們需要采用不同的參數去運行。

    經過概括,參數基本上分為兩種類型:

    • 單個獨立參數:例如前面的第一種場景,我們只需要變換搜索關鍵字這一個參數
    • 多個具有關聯性的參數:例如前面的第二種場景,我們需要變換用戶名和密碼兩個參數,并且這兩個參數需要關聯組合

    然后,對于參數而言,我們可能具有一個參數列表,在腳本運行時需要按照不同的規則去取值,例如順序取值、隨機取值、循環取值等等。

    對于這一塊兒,沒有太多新的概念,這就是典型的參數化和數據驅動。遺憾的是,當前HttpRunner并未支持該功能特性。

    考慮到該需求的普遍性,并且近期提到該需求的的人也越來越多(issue #74issue #87issue #88issue #97),因此趁著春節假期的空閑時間,決定優先實現下。

    經過前面的場景分析,我們的目標已經很明確了,接下來就是如何實現的問題了。

    借鑒 LoadRunner 的數據參數化

    要造一個輪子,最好是先看下現有知名輪子的實現機制。之前有用過一段時間的 LoadRunner,對其參數化機制印象蠻深的,雖然它是性能測試工具,但在腳本參數化方面是通用的。

    我們先看下在 LoadRunner 中是如何實現參數化的。

    在 LoadRunner 中,可以在腳本中創建一個參數,然后參數會保存到一個.dat的文件中,例如下圖中的psd.dat。

    .dat文件中,是采用表格的形式來存儲參數值,結構與CSV基本一致。

    對于單個獨立參數,可以將參數列表保存在一個單獨的.dat文件中,第一行為參數名稱,后續每一行為一個參數值。例如本文背景介紹中的第一類場景,數據存儲形式如下所示:

    
    					
    1
    2
    3
    4
    
    					
    Keyword
    hello
    world
    debugtalk

    然后對于參數的取值方式,可以通過Select next rowUpdate value on進行配置。

    Select next row的可選方式有:

    • Sequential:順序取值
    • Random:隨機取值
    • Unique:為每個虛擬用戶分配一條唯一的數據

    Update value on的可選方式有:

    • Each iteration:每次腳本迭代時更新參數值
    • Each occurrence:每次出現參數引用時更新參數值
    • Once:每條數據只能使用一次

    而且,可以通過對這兩種方式進行組合,配制出9種參數化方式。

    另外,因為 LoadRunner 本身是性能測試工具,具有長時間運行的需求,假如Select next row選擇為Unique,同時Update value on設置為Each iteration,那么就會涉及到參數用完的情況。在該種情況下,可通過When out of value配置項實現如下選擇:

    • Abort vuser:當超出時終止腳本
    • Continue in a cyclic manner:當超出時回到列表頭再次取值
    • Continue with last value:使用參數表中的最后一個值

    對于多個具有關聯性的參數,可以將關聯參數列表保存在一個.dat文件中,第一行為參數名稱,后續每一行為一個參數值,參數之間采用逗號進行分隔。例如本文背景介紹中的第二類場景,數據存儲形式如下所示:

    
    					
    1
    2
    3
    4
    
    					
    UserName,Password
    test1,111111
    test2,222222
    test3,333333

    對于參數的取值方式,與上面單個獨立參數的取值方式基本相同。差異在于,我們可以只配置一個參數(例如UserName)的取值方式,然后其它參數(例如Password)的取值方式選擇為same line as UserName。如此一來,我們就可以保證參數化時的數據關聯性。

    LoadRunner 的參數化機制就回顧到這里,可以看出,其功能還是很強大的,使用也十分靈活。

    設計思路演變歷程

    現在再回到我們的 HttpRunner,要如何來實現參數化機制呢?

    因為 LoadRunner 的參數化機制比較完善,用戶群體也很大,因此我在腦海里最先冒出的想法就是照抄 LoadRunner,將 LoadRunner 在 GUI 中配置的內容在 HttpRunner 中通過YAML/JSON來進行配置。

    按照這個思路,在 HttpRunner 的 config 中,就要有一塊兒地方用來進行參數化配置,暫且設定為parameters吧。然后,對于每一個參數,其參數列表要單獨存放在文件中,考慮到LoadRunner中的.dat文件基本就是CSV格式,因此可以約定采用大眾更熟悉的.csv文件來存儲參數;在腳本中,要指定參數變量從哪個文件中取值,那么就需要設定一個parameter_file,用于指定對應的參數文件路徑。接下來,要實現取值規則的配置,例如是順序取值還是隨機取值,那么就需要設定select_next_rowupdate_value_on。

    根據該設想,在YAML測試用例文件中,數據參數化將描述為如下形式:

    
    					
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    					
    - config:
    name: "demo for data driven."
    parameters:
    - Keyword:
    parameter_file: keywords.csv
    select_next_row: Random
    update_value_on: EachIteration
    - UserName:
    parameter_file: account.csv
    select_next_row: Sequential
    update_value_on: EachIteration
    - Password:
    parameter_file: account.csv
    select_next_row: same line as UserName

    這個想法基本可行,但就是感覺配置項有些繁瑣,我們可以嘗試再對其進行簡化。

    首先,比較明顯的,針對每個參數都要配置select_next_rowupdate_value_on,雖然從功能上來說比較豐富,但是對于用戶來說,這些功能并不都是必須的。特別是update_value_on這個參數,絕大多數情況下我們的需求應該都是采用Each iteration,即每次腳本再次運行時更新參數值。因此,我們可以去除update_value_on這個配置項,默認都是采用Each iteration的方式。

    經過第一輪簡化,配置描述方式變為如下形式:

    
    					
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    					
    - config:
    name: "demo for data driven."
    parameters:
    - Keyword:
    parameter_file: keywords.csv
    select_next_row: Random
    - UserName:
    parameter_file: account.csv
    select_next_row: Sequential
    - Password:
    parameter_file: account.csv
    select_next_row: same line as UserName

    然后,我們可以看到UserNamePassword這兩個參數,它們有關聯性,但卻各自單獨進行了配置;而且對于有關聯性的參數,除了需要對第一個參數配置取值方式外,其它參數的select_next_row應該總是為same line as XXX,這樣描述就顯得比較累贅了。

    既然是有關聯性的參數,那就放在一起吧,參數名稱可以采用約定的符號進行分離??紤]到參數變量名稱通常包含字母、數字和下劃線,同時要兼顧YAML/JSON中對字符的限制,因此選擇短橫線(-)作為分隔符吧。

    經過第二輪簡化,配置描述方式變為如下形式:

    
    					
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    					
    - config:
    name: "demo for data driven."
    parameters:
    - Keyword:
    parameter_file: keywords.csv
    select_next_row: Random
    - UserName-Password:
    parameter_file: account.csv
    select_next_row: Sequential

    接著,我們再看下parameter_file參數。因為我們測試用例中的參數名稱必須與數據源進行綁定,因此這一項信息是不可少的。但是在描述形式上,還是會感覺有些繁瑣。再一想,既然我們本來就要指定參數名稱,那何必不將參數名稱約定為文件名稱呢?

    例如,對于參數Keyword,我們可以將其數據源文件名稱約定為Keyword.csv;對于參數UserNamePassword,我們可以將其數據源文件名稱約定為UserName-Password.csv;然后,再約定數據源文件需要與當前YAML/JSON測試用例文件放置在同一個目錄。

    經過第三輪簡化,配置描述方式變為如下形式:

    
    					
    1
    2
    3
    4
    5
    6
    7
    
    					
    - config:
    name: "demo for data driven."
    parameters:
    - Keyword:
    select_next_row: Random
    - UserName-Password:
    select_next_row: Sequential

    同時該用例文件同級目錄下的數據源文件名稱為Keyword.csvUserName-Password.csv。

    現在,我們就只剩下select_next_row一個配置項了。既然是只剩一項,那就也省略配置項名稱吧。

    最終,我們的配置描述方式變為:

    
    					
    1
    2
    3
    4
    5
    
    					
    - config:
    name: "demo for data driven."
    parameters:
    - Keyword: Random
    - UserName-Password: Sequential

    不過,我們還忽略了一個信息,那就是腳本的運行次數。假如參數取值都是采用Sequential的方式,那么我們可以將不同組參數進行笛卡爾積的組合,這是一個有限次數,可以作為自動化測試運行終止的條件;但如果參數取值采用Random的方式,即每次都是在參數列表里面隨機取值,那么就不好界定自動化測試運行終止的條件了,我們只能手動進行終止,或者事先指定運行的總次數,不管是采用哪種方式,都會比較麻煩。

    針對參數取值采用Random方式的這個問題,我們不妨換個思路。從數據驅動的角度來看,我們期望在自動化測試時能遍歷數據源文件中的所有數據,那么重復采用相同參數進行測試的意義就不大了。因此,在選擇Random的取值方式時,我們可以先將參數列表進行亂序排序,然后采用順序的方式進行遍歷;對于存在多組參數的情況,也可以實現亂序排序后再進行笛卡爾積的組合方式了。

    到此為止,我們的參數化配置方式應該算是十分簡潔了,而且在功能上也能滿足常規參數化的配置需求。

    最后,我們再回過頭來看腳本參數化設計思路的演變歷程,基本上都可以概括為約定大于配置,這的確也是HttpRunner崇尚和遵循的準則。

    開發實現

    設計思路理順了,實現起來就比較簡單了,點擊此處查看相關代碼,就會發現實際的代碼量并不多。

    在這里我就只挑幾個典型的點講下。

    數據源格式約定

    既然是參數化,那么肯定會存在數據源的問題,我們約定采用.csv文件格式來存儲參數列表。同時,在同一個測試場景中可能會存在多個參數的情況,為了降低問題的復雜度,我們可以約定獨立參數存放在獨立的.csv文件中,多個具有關聯性的參數存放在一個.csv文件中。另外,我們同時約定在.csv文件中的第一行必須為參數名稱,并且要與文件名保持一致;從第二行開始為參數值,每個值占一行。

    例如,keyword這種獨立的參數就可以存放在keyword.csv中,內容形式如下:

    
    					
    1
    2
    3
    4
    
    					
    keyword
    hello
    world
    debugtalk

    usernamepassword這種具有關聯性的參數就可以存放在username-password.csv中,內容形式如下:

    
    					
    1
    2
    3
    4
    
    					
    username,password
    test1,111111
    test2,222222
    test3,333333

    csv 解析器

    數據源的格式約定好了,我們要想進行讀取,那么就得有一個對應的解析器。因為我們后續想要遍歷每一行數據,并且還會涉及到多個參數進行組合的情況,因此我們希望解析出來的每一行數據應該同時包含參數名稱和參數值。

    于是,我們的數據結構就約定采用list of dict的形式。即每一個.csv文件解析后會得到一個列表(list),而列表中的每一個元素為一個字典結構(dict),對應著一行數據的參數名稱和參數值。具體實現的代碼函數為_load_csv_file。

    例如,上面的username-password.csv經過解析,會生成如下形式的數據結構。

    
    					
    1
    2
    3
    4
    5
    
    					
    [
    {'username': 'test1', 'password': '111111'},
    {'username': 'test2', 'password': '222222'},
    {'username': 'test3', 'password': '333333'}
    ]

    這里還會涉及到一個問題,就是參數取值順序。

    YAML/JSON測試用例中,我們會配置參數的取值順序,是要順序取值(Sequential)還是亂序隨機取值(Random)。對于順序的情況沒啥好說的,默認從.csv文件中讀取出的內容就是順序的;對于隨機取值,更確切地說,應該是亂序取值,我們需要進行一次亂序排序,實現起來也很簡單,使用random.shuffle函數即可。

    
    					
    1
    2
    
    					
    if fetch_method.lower() == "random":
    random.shuffle(csv_content_list)

    多個參數的組合

    然后,對于多個參數的情況,為了組合出所有可能的情況,我們就需要用到笛卡爾積的概念。直接看例子可能會更好理解些。

    例如我們在用例場景中具有三個參數,a為獨立參數,參數列表為[1, 2];xy為關聯參數,參數列表為[[111,112], [121,122]];經過解析后,得到的數據分別為:

    
    					
    1
    2
    3
    4
    5
    6
    7
    8
    
    					
    a:
    [{"a": 1}, {"a": 2}]
     
    x & y:
    [
    {"x": 111, "y": 112},
    {"x": 121, "y": 122}
    ]

    那么經過笛卡爾積,就可以組合出4種情況,組合后的結果應該為:

    
    					
    1
    2
    3
    4
    5
    6
    
    					
    [
    {'a': 1, 'x': 111, 'y': 112},
    {'a': 1, 'x': 121, 'y': 122},
    {'a': 2, 'x': 111, 'y': 112},
    {'a': 2, 'x': 121, 'y': 122}
    ]

    這里需要強調的是,多個參數經過笛卡爾積運算轉換后,仍然是list of dict的數據結構,列表中的每一個字典(dict)代表著參數的一種組合情況。

    參數化數據驅動

    現在,我們已經實現了在YAML/JSON測試用例文件中對參數進行配置,從.csv數據源文件中解析出參數列表,并且生成所有可能的組合情況。最后還差一步,就是如何使用參數值來驅動測試用例的執行。

    聽上去很高大上,但實際卻異常簡單,直接對照著代碼來說吧。

    對于每一組參數組合情況來說,我們完全可以將其視為當前用例集運行時定義的變量值。而在 HttpRunner 中每一次運行測試用例集的時候都需要對runner.Runner做一次初始化,里面會用到定義的變量(即config_dict["variables"]),那么,我們完全可以在每次初始化的時候將組合好的參數作為變量傳進去,假如存在同名的變量,就進行覆蓋。

    這樣一來,我們就可以使用所有的參數組合情況來依次驅動測試用例的執行,并且每次執行時都采用了不同的參數,從而也就實現了參數化數據驅動的目的。

    效果展示

    最后我們再來看下實際的運行效果吧。

    假設我們有一個獲取token的接口,我們需要使用 user_agent 和 app_version 這兩個參數來進行參數化數據驅動。

    YAML 測試用例的描述形式如下所示:

    
    					
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    					
    - config:
    name: "user management testset."
    parameters:
    - user_agent: Random
    - app_version: Sequential
    variables:
    - user_agent: 'iOS/10.3'
    - device_sn: ${gen_random_string(15)}
    - os_platform: 'ios'
    - app_version: '2.8.6'
    request:
    base_url: $BASE_URL
    headers:
    Content-Type: application/json
    device_sn: $device_sn
     
    - test:
    name: get token with $user_agent and $app_version
    api: get_token($user_agent, $device_sn, $os_platform, $app_version)
    extract:
    - token: content.token
    validate:
    - "eq": ["status_code", 200]
    - "len_eq": ["content.token", 16]

    其中,user_agent 和 app_version 的數據源列表分別為:

    
    					
    1
    2
    3
    4
    
    					
    user_agent
    iOS/10.1
    iOS/10.2
    iOS/10.3
    
    					
    1
    2
    3
    
    					
    app_version
    2.8.5
    2.8.6

    那么,經過笛卡爾積組合,應該總共有6種參數組合情況,并且 user_agent 為亂序取值,app_version 為順序取值。

    最終的測試結果如下所示:

    
    					
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    					
    $ hrun tests/data/demo_parameters.yml
     
    Running tests...
    ----------------------------------------------------------------------
    get token with iOS/10.2 and 2.8.5 ... INFO:root: Start to POST http://127.0.0.1:5000/api/get-token
    INFO:root: status_code: 200, response_time: 13 ms, response_length: 46 bytes
    OK (0.014845)s
    get token with iOS/10.2 and 2.8.6 ... INFO:root: Start to POST http://127.0.0.1:5000/api/get-token
    INFO:root: status_code: 200, response_time: 2 ms, response_length: 46 bytes
    OK (0.003909)s
    get token with iOS/10.1 and 2.8.5 ... INFO:root: Start to POST http://127.0.0.1:5000/api/get-token
    INFO:root: status_code: 200, response_time: 3 ms, response_length: 46 bytes
    OK (0.004090)s
    get token with iOS/10.1 and 2.8.6 ... INFO:root: Start to POST http://127.0.0.1:5000/api/get-token
    INFO:root: status_code: 200, response_time: 5 ms, response_length: 46 bytes
    OK (0.006673)s
    get token with iOS/10.3 and 2.8.5 ... INFO:root: Start to POST http://127.0.0.1:5000/api/get-token
    INFO:root: status_code: 200, response_time: 3 ms, response_length: 46 bytes
    OK (0.004775)s
    get token with iOS/10.3 and 2.8.6 ... INFO:root: Start to POST http://127.0.0.1:5000/api/get-token
    INFO:root: status_code: 200, response_time: 3 ms, response_length: 46 bytes
    OK (0.004846)s
    ----------------------------------------------------------------------
    Ran 6 tests in 0.046s

    至此,我們就已經實現了參數化數據驅動的需求。對于測試用例中參數的描述形式,大家要是發現還有更加簡潔優雅的方式,歡迎反饋給我。

    原文轉自:http://debugtalk.com/post/httprunner-data-driven/

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>