天天看點

Web自動化測試架構實戰-基于unittest

随着自動化腳本數量的增加,用例及測試資料的組織和維護,公共子產品的複用,用例挑選及執行控制,或者團隊協作及用例編寫規範化,我們便需要引入自動化測試架構。

架構是應用的組織架構,一般包含代碼、配置、資料、日志、依賴的組織,可複用子產品的抽取以及運作控制等。就像從一盤散沙的武裝人員,到一個軍隊。架構是由腳本集合發展到應用(包含測試項目)的一種必然選擇。

架構的基本功能一般包括:

代碼、配置檔案、資料檔案等的分類組織。

依賴管理。

公共子產品的複用。

運作流程及控制。

另外,從設計目标來看,架構應該具有易用、健壯穩定、良好的性能,通過配置或參數提供靈活的使用方式,以及易調試和維護(如提供運作日志)等特性。

測試架構是為測試而設計的架構。測試架構一般還要包含用例編寫、豐富的斷言方法、用例組織、測試準備和清理方法、執行測試、生成測試報告等功能。

相比于一堆腳本集合,測試架構一般具有以下優點。

用例執行互相獨立,一個用例失敗不影響其他用例執行和驗證。

清晰的用例執行狀态,如成功、失敗、出錯、跳過等以及系統的測試報告。

靈活的用例挑選及批量運作。

提供豐富的斷言(期望結果與實際結果的對比)方法。

提供不同範圍的測試準備和清理方法。

在特定環境不滿足條件時,跳過用例。

python中常用的測試架構有unittest、nose、pytest以及robot framework等。其中unittest是python自帶的測試架構,提供基礎的用例管理和測試控制功能,使用靈活易于定制。本章以unittest及selenium為基礎來講解web自動化測試架構的搭建流程。pytest和robot framework将在其他章進行講解。

unitest是python自帶的單元測試架構,依照junit編寫,和其他語言的的主流單元測試框有着相似的風格。unittest中主要包含testcase測試用例、testsuite測試套件、testfixture測試準備及清理方法和testrunner測試運作器等主要概念,另外還包含testloader用于批量加載用例生成測試套件,testresult用于在testrunner中記錄測試結果。unittest的模型關系及運作流程如圖9.1所示。

Web自動化測試架構實戰-基于unittest

圖9.1 unittest模型關系及運作流程

編寫測試用例時,需要編寫一個類(一般約定以test開頭),并繼承unittest.testcase。每個測試類包含測試方法(測試用例)及testfixture測試準備和清理方法兩部分。測試方法一般約定為名稱已test開頭(測試類中非已test開頭的方法不作為用例執行,一般可以用作輔助步驟等)。每個測試方法在執行時會執行個體化為一個testcase對象來運作。同時測試用例父類unittest.testcase中提供了豐富的斷言(測試對比)方法來驗證結果。

testfixture也譯做測試腳手架或者測試夾具。包含setup測試準備和teardown測試清理兩類方法,分别在用例執行前執行和用例執行後執行,像三明治的兩片面包一樣将我們的正菜測試用例夾在其中。testfixture提供不同範圍的測試準備和清理,包含測試用例級、測試類級和測子產品級。

testsuite測試套件用來挑選群組織用例。除了使用測試套件自帶的addtest添加用例方法添加用例外,還可以使用testloader用例加載器對象來批量添加一個測試子產品、一個測試類等快速生成一個測試套件對象。同時測試套件支援嵌套,執行時按用例及子套件添加順序進行深度周遊執行。

testrunner用于測試套件、其中的用例各個級别的testfixture的運作控制及異常處理。testrunner中包含一個testresult對象,穿梭于各個用例及套件中記錄并實時輸出結果。

unittest測試用例編寫一般應遵循以下步驟。

(1)測試腳本(測試子產品)約定以test開頭。

(2)測試類約定以test開頭,并繼承unittest.testcase。

(3)測試方法(測試用例)約定以test開頭,可以包含多個步驟,及多個斷言。

注意:測試類中不應覆寫父類的__init__方法,否則會破壞unittest将每個測試方法生成測試用例對象的過程。

測試類編寫示例如下。

上例測試類中的add方法非test開頭,不作為用例運作,一般測試類中的非測試方法可以作為輔助步驟供測試方法調用。

測試方法可以使用docstring(一對三個雙引号中間的字元串),來提供用例描述。用例描述會詳細模式下顯示。

<code>self.assertequal(s,3)</code>是父類<code>unittest.testcase</code>提供的一種斷言方法,用于斷言相等。在用例中也可以直接使用python自帶的assert語句,如<code>assert s==3</code>。但是相比來說使用testcase類提供的斷言方法,在失敗資訊中可以檢視到更清晰的結果對比。

<code>if __name__ == '__main__'</code>表示如果本子產品獨立執行(非其他子產品調用),<code>unittest.main()</code>為目前腳本提供一個指令行接口(支援多種運作參數),在運作腳本時預設運作目前測試子產品按約定規則(測試類以test開頭,測試方法以test開頭)所能發現的所有用例。

執行腳本,運作結果如下。

最上面的“..f”表示,前兩條用例通過,第三條用例失敗。unittest.main()中可以通過參數verbosity來控制顯示結果的詳細級别,支援0,1,2三級,預設為1。此時通過的用例顯示為“.”,失敗的用例顯示為“f”,出錯的用例顯示為“e”。失敗和出錯的差別為失敗始終實際結果和期望結果不一緻導緻的assertionerror斷言異常。出錯指用例運作中遇到了其他未捕獲的異常而時用例執行被迫中斷。

當用例失敗或出錯時會顯示詳細的追溯資訊及運作中的實際結果與期望結果。如上例中在斷言下展示了實際結果是“101”而期望結果是“11”,是以導緻了斷言失敗。

運作最後是運作概要資訊。如上例共運作3條用例,用時0.001秒,總體狀态為失敗,失敗用例1條。

unittest.main()參數verbosity為0時,不顯示每條用例的執行情況,隻顯示報錯資訊。<code>verbosity=2</code>時則顯示每條用例的名稱或描述及執行情況。

注意:用例執行順序并非按定義編寫順序執行,而是按用例方法名的ascii碼排序後執行。

修改<code>unittest.main()為unittest.main(verbosity=2)</code>,重新運作結果如下。

在verbosity=2詳盡結果模式下,每條用會顯示其測試方法名,所屬子產品及測試類、docstring測試描述以及執行狀态。“ok”表示通過,“fail”表示失敗。

注:除了使用類來編寫測用例外,unittest還支援将函數形式的測試代碼轉換為測試用例對象,示例如下。

一般來說,推薦使用測試類的寫法。

表9.1 testcase常用斷言方法

斷言方法

解釋

示例

assertequal(a, b)

斷言a和b值相等

self.assertequal(1+1, 2)

assertnotequal(a, b)

斷言a和b值不相等

self.assertequal(1+2, 2)

asserttrue(x)

斷言x是true

self.asserttrue(2&gt;1)

assertfalse(x)

斷言x的false

self.asserttrue(2&lt;1)

assertis(a, b)

斷言a和b是同一個對象

self.assertis(1, 1)

assertisnot(a, b)

斷言a和b不是同一個對象

self.assertisnot(1, true)

assertisnone(x)

斷言x是none

self.assertisnone({}.get('a'), none)

assertisnotnone(x)

斷言x不是none

self.assertisnotnone({'a':1}.get('a'), none)

assertin(a, b)

斷言a在b中

self.assertin('a', 'abc')

assertnotin(a, b)

斷言a不在b中

self.assertnotin(1, [2,3,4])

assertisinstance(a, b)

斷言a是b類型

self.assertisinstance(1, int)

assertnotisinstance(a, b)

斷言a不是b類型

self.assertnotisinstance([], dict)

在測試中,對同一測試流程使用不同的測試資料是否非常必要的。

當同一個用例需要測試多組資料時,在循環時為防止某組資料測試失敗導緻執行中斷而後面的用例未進行測試可以在循環中使用subtest來確定所有資料都被執行。示例如下。

執行時會顯示所有失敗的資料。

使用subtest後多條資料仍表現為一條用例,如果想一條用例根據不同的資料動态生成多條用例,則可以使用三方庫ddt實作。安裝方法如下。

使用示例如下。

使用ddt需要在測試類上添加裝飾器@ddt.ddt,需要資料驅動的測試方法上添加裝飾器<code>@ddt.data(資料1, 資料2, 資料3, ...)</code>,用例中添加參數來接收每一個資料。

運作時将動态生成多條用例,實際運作結果如下。

對于嵌套的資料,如。

也可以使用@ddt.unpack解包,自動将每一組資料拆分成多個變量,示例如下。

兩個示例執行效果相同。

unittest測試用例支援在某些條件不滿足時跳過測試用例或者整個測試類。可以設定無條件跳過(比如功能尚未實作、廢棄或者bug未修複)或者根據條件跳過(比如環境條件不具備、依賴步驟失敗等),官網示例如下。

執行結果如下。

有些用例是反向用例,即正常情況下用例就應該執行失敗,此時實際是符合我們的預期的(測試通過),此時為了結果展現的正确性,我們需要标記用例為期望失敗,示例如下。

在測試中測試準備和清理是執行測試必不可少的一部分。測試準備一般稱為setup,測試清理一般稱為teardown(一般也譯作拆卸)。unittest提供三種範圍的測試準備和清理方法,分别為。

setup()、teardown():測試方法(用例)級,類中的每個用例執行前和執行後執行。

setupclass()、teardownclass():測試類級,本類全部用例執行前,和全部執行後執行一次。

setupmodule()、teardownmodule():測試子產品級,本測試子產品所有用例執行前、所有用例執行後執行一次。

示例如下。

其中<code>setupmodule和teardownmodule</code>是子產品中的函數,無參無返。<code>setupclass和teardownclass</code>必須是類方法,使用<code>@classmethod</code>裝飾。<code>setup</code>和<code>teardown</code>是正常的執行個體(對象)方法。

用例單獨的測試準備可以寫在用例方法中,正式步驟之前。用例單獨的測試清理方法則建議使用<code>self.addcleanup(function, *args, **kwargs)</code>來添加,以防止斷言失敗或測試步驟異常導緻清理方法不被執行。

運作結果如下。

注意:setupmodule、teardownmodule、setupclass、teardownclass、setup、teardown這些是固定的關鍵字,大小寫敏感,名稱不能寫錯。

setup、setupclass、setupmodule方法異常時,其範圍内的測試用例将不會執行。而無論用例是否異常,teardown、teardownclass、teardownmodule都會執行。

實際上用例很少單獨執行。有時我們需要運作所有用例,或者通常我們需要挑選一批用例進行執行。為了實作跨測試類、跨測試子產品來挑選用例,便引入了testsuite測試套件的概念。testloader則可以快速将測試子產品、測試類及一批用例名稱加載生成測試套件對象。

testsuite是有序的用例合集,同時嵌套子的套件,即可以快速将兩個不同的測試套件組合成一個新的測試套件。

testsuite可以通過addtest添加用例或者子套件,或者通過addtests一次添加多個用例或子套件。另外也可以在執行個體化時通過清單傳入要添加的用例及子套件,完成添加。

注意:不同于unittest.main()将用例按用例方法名ascii順序執行,測試套件是按添加順序疊代執行的。

testloader即用例加載器,用于收集用例并生成測試套件對象。常用的方法如下。

discover(start_dir, pattern='test*.py', top_level_dir=none):按目錄遞歸搜集用例,從start_dir目錄及所有子包中,按指定pattern比對的腳本檔案中收集測試用例,并生成具有嵌套結構的測試套件對象。

loadtestsfrommodule(module, pattern=none):加載一個測試子產品中的所有測試用例,生成測試套件對象。

loadtestsfromtestcase(testcaseclass):加載一個測試類中的所有測試用例,生成測試套件對象

loadtestsfromname(name, module=none):按描述名稱搜集測試用例,生成測試套件對象。

loadtestsfromnames(names, module=none) 按多個描述名稱搜集測試用例,生成的測試套件對象。

以上示例中示範了用力加載器的幾種使用方法。除了loadtestsfromtestcase是使用類對象外,其他都使用字元串格式的描述來描述要導入的子產品或用例發現路徑。

discover一般用的比較多,可以快速周遊中一個目錄中的所有用例,以及子包的用例,注意子包中要包含__init__.py檔案才會被周遊。

除了調試時使用unittest.main()運作測試子產品所有的用例外,還可以通過unittest的指令行接口發現并運作用例。或者自己編寫運作腳本,使用testrunner對象運作組裝好的測試套件。

由于unittest沒有額外生成可執行檔案,是以調用unittest可以使用python -m unittest或者python3 -m unittest指令。

常用的指令如下。

python -m unittest tests:周遊運作整個tests目錄及子包用例

python -m unittest discover tests:同上,周遊運作整個tests目錄及子包用例,支援discover參數。

python3 -m unittest tests/test_demo5.py:執行tests目錄下的test_demo5.py中的所有用例。

python3 -m unittest tests.test_demo5 tests.test_demo6:執行多個測試子產品

python3 -m unittest tests.test_demo5.testdemo5:執行指定測試子產品的指定測試類。

python3 -m unittest tests.test_demo5.testdemo5.test_a:執行指定測試子產品,指定測試類中的指定用例。

unittest指令行還支援以下運作參數。

-v:詳細結果模式。

-b,--buffer:捕獲用例中的print資訊并獨立輸出。

-f,--failfast:遇到出錯或失敗立即停止(而不運作完所有用例)。

-k:運作用例路徑(包.子產品.類.測試方法)中包含指定字元串的用例,大小寫敏感,支援*通配符,如-k foo可以比對到 foo_tests.sometest.test_something 和 bar_tests.sometest.test_foo ,但是不能比對到 bar_tests.footest.test_something 。

--local:在報錯回溯資訊中顯示測試方法的局部變量。

代碼運作即使用自定義的運作腳本,自定義組裝測試套件,并使用testrunner運作。

基本示例如下。

由于預設的texttestrunner隻能輸出文本形式的測試報告,要生成html形式的報告我們需要三方的插件,相關的插件比較多,如單檔案的htmltestrunner.py、可以直接安裝的三方庫htmlrunner、htmlreport等,這裡以htmlrunner為例講解。

安裝方法為。

簡單使用方法如下。

預設報告樣式如圖9.2所示。

Web自動化測試架構實戰-基于unittest

圖9.2 htmlrunner測試報告預設樣式

IT

繼續閱讀