1. 程式人生 > 實用技巧 >技術面試沒過,居然是沒有用pytest測試框架

技術面試沒過,居然是沒有用pytest測試框架

技術面試沒過,居然是沒有用這個測試框架

1、引言

我有一個朋友是做Python自動化測試的。前幾天他告訴我去參加一個大廠面試被刷了。

我問他是有沒有總結被刷下來的原因。他說面試官問了一些 pytest 單元測試框架相關的知識,包括什麼外掛系統和用力篩選。但是他所在的公司用的技術是基於 unittest 的,沒有用過 pytest。

我跟他說你可以和技術面試官說明,在實際過程當中你沒有使用過 pytest,但是你可以後面再學。這哥們說:我就是這樣跟面試官說的,但是面試官告訴我 pytest 現在已經是行業裡面的主流,還在堅持用 unittest 說明我的技術已經過時了,沒有跟上現在測試領域的發展。

實際上他在面試之前已經瞭解過 pytest 的一些基礎用法,但是網上的一些資料,都是停留在用法和一些知識點的講解,沒有深入到 pytest 內部執行和一些高階特性。所以被問到的時候,自己臨時抱佛腳的一些知識都沒有用上。

後面我給這位朋友補習了一些關於Python的高階特性。現在我連同基礎部分的內容一起貼出來,希望對Python自動化測試的一些朋友有所幫助。

2、為什麼用單元測試框架?

首先我要說明一下什麼是單元測試框架?

unittest 和 pytest 都是單元測試框架。單元測試指的是在程式設計過程當中形成的對函式或者是類下面的方法進行測試的一個過程。

在不使用任何框架的前提下,其實也是可以進行單元測試的。比如我們可以通過 if 判斷 、異常處理或者是其他的流程控制來表示測試是否通過。

defadd(a,b):
returna+b

deftest_add():
ret=add(3,4)
ifret==7:
print("add函式的測試通過")
else:
print("add函式的測試失敗")

如果要執行這個用例,需要手工呼叫 test_add 這個函式:

test_add()

接下來,使用 python 執行這個檔案,就能得到測試結果:

pythontest_add.py

雖然說上面我們通過 if 判斷,對一個函式進行了測試,而且得到了測試結果,但是流程是比較複雜的:

  • 首先我們需要人工去判斷結果,
  • 第2我們需要通過 Python去執行模組。
  • 第3,我們還需要顯性的去呼叫 test_add 這個函式。

這還只是在我們只測試了一個函式的情況下,當需要測試的函式或者類越來越多的時候,這個過程會越來越複雜。

而使用單元測試框架,可以極大的簡化我們對單元測試的過程,使用單元測試框架以後,框架會幫我們自動去收集用例、執行用例、生成報告。

framework

3、pytest 的基礎使用

上面的測試程式碼使用 pytest 編寫,可以這樣寫。

defadd(a,b):
returna+b

deftest_add():
assert7==add(3,4)

寫完測試用例以後,我們只需要在目錄下輸入pytest 指令,就可以自動執行用例,而且呢結果會直接顯示在命令列的下方。

image-20201219192616608

上面講的是單元測試過程,也就是說對某個函式或者是類下面的方法進行測試,有的人可能會不理解。在實際工作過程當中很少進行單元測試啊,測試人員做的更多的是介面測試,UI測試,pytest 怎麼用呢?

實際上不管是介面測試還是UI測試,都是可以使用 pytest。當你進行介面測試的時候,你只需要把訪問介面的過程封裝成一個Python函式。

defvisit_api():
print("訪問介面,得到結果...")
returnresponse

deftest_api():
assertexpected_response==visit_api()

當你進行 web測試的時候,你只需要操作瀏覽器的過程封裝成一個函式?

defbrowser_method():
print("點點點")
returnui_response

deftest_web():
assertexpected_response==browser_method()

在這種情況下。介面訪問和web操作都是以函式形式存在的,我們直接去測試這個 Python 函式,也是一個單元測試的過程。

因為 pytest 是一個第三方的框架,所以我們先要安裝。安裝方式非常簡單,只需要通過 pip 這個包管理工具安裝就可以了。

pipinstall-Upytest

安裝完成以後,我們可以向使用上面的那個例子一樣:

  • 第1步:定義一個測試函式,這個測試函式通常會呼叫被測函式。
  • 第2步:使用assert斷言,assert 後面可以跟任意的 Python 條件表示式。
assert4<5
assert"yuze"in"yuzewang"
assertisinstance(6,int)

測試用例函式有 2 個注意事項:

  • 函式名稱以test_開頭;
  • 儲存測試用例的檔案以test_*.py 的形式或者 *_test.py 的形式。

例行用了以後呢,在命令行當中會顯示4個部分的內容:

  • 第1個部分,測試用例和通過的結果,
  • 第2個部分,失敗用例回溯資訊,
  • 第3個部分,輸出捕獲資訊,
  • 第4個部分,總結資訊。

image-20201224195304229

在拍test當中通過的測試用例,不會有特別詳細的結果,但是這是失敗的測試用例預設會有非常詳細的結果,而且會幫你捕獲錯誤原因。

4、測試夾具(Fixture)是什麼?

在測試過程當中,有時你需要提前給你的測試用例去準備一個執行環境。這個測試環境通常來說被稱為測試夾具(Fixture),又被稱為固定裝置、測試韌體等。

fixture

  • 當你要測試一個電器的時候,你需要提供不同的輸入電壓電流的環境,
  • 當你測試一臺電腦網路的時候,必須要提供網路環境,
  • 當你要測試一個手機遊戲能否被安裝時,你需要提供一臺手機環境,
  • 當你要測試一個軟體能否登入的時候,你需要準備使用者名稱和密碼這樣的使用者環境,
  • 當你要測試一個數據庫能否操作的時候,需要提供資料庫的連線環境。

現在我們來舉一個夾具的例子,我們需要測試一個註冊的函式。這個註冊函式提供兩個引數,第1個引數是手機號,第2個引數是密碼。註冊函式的邏輯就是對手機號碼和密碼進行校驗,如果通過校驗表示註冊成功,如果不通過表示註冊失敗。

defis_mobile(number:str):
ifre.search(r"^1[3-9][0-9]{9}$",number):
returnTrue
returnFalse

defregister(mobile,password):
ifis_mobile(mobile)andlen(password)>=6:
return"success"
return"fail"

deftest_register():
assert"success"==register("13177778888","123456")

這個測試用例並沒有什麼問題,但是它存在優化的空間。一個優化的空間是每個手機號碼都是我們手工生成的,當需要編寫多組資料測試時,會有一點費時間。現在我們可以編寫一個函式,自動生成一個手機號碼,當我有多組資料需要測試的時候,我只需要重複呼叫生成手機號碼的函式,就可以獲取測試資料。這個生成手機號碼的函式呢,就是一個夾具。

defgen_a_mobile():
"""隨機生成13開頭的手機號碼。"""
random_num="".join([str(random.randint(1,9))foriinrange(9)])
return"".join(["13",random_num])

pytest 提供了一種叫做依賴注入的機制,當一個函式被宣告為夾具的時候,可以在測試函式中傳入這個夾具的名稱,pytest會自動呼叫它。

importrandom
importpytest

@pytest.fixture
defgen_a_mobile():
"""隨機生成13開頭的手機號碼。"""
random_num="".join([str(random.randint(1,9))foriinrange(9)])
return"".join(["13",random_num])

deftest_register(gen_a_mobile):
assert"success"==register(gen_a_mobile,"123456")

pytest 當中的夾具系統非常非常的靈活,後面如果有時間的我專門寫文章跟大家講解夾具系統。

5、資料驅動和引數化

現在我們編寫的函式和測試用例是1對1的關係,也就是說,當你想測試某個功能場景的時候,你必須要去編寫一個對應的測試函式。當測試的場景越來越多,測試資料越來越複雜的情況下,需要編寫更多的測心率函式,而這些函式的邏輯基本上是重複的。

image-20201221154754539

在 pytest 當中可以使用引數化這種測試手段,簡化編寫用例函式的過程。我們並不需要為每一組測試資料單獨去編寫一個測試函式,而是採取多種資料共用一個函式的方式。如果測試操作幾乎一致,可以重複使用這一個函式進行測試。

importpytest

cases=[
(1,2,3),
("hello","world","helloworld"),
(1,"world","1world")
]


@pytest.mark.parametrize("a,b,expected",cases)
deftest_add(a,b,expected):
assertexpected==add(a,b)

在這個例子當中,cases這個變數儲存了三組測試用例的資料,每一組測試資料用一個元組表示,元組的第1個元素代表a,第2個元素代表B,第3個元素代表預期結果。

image-20201221160111259

@pytest.mark.parametrize("a,b,expected", cases) 這一行程式碼的意思是說,每一次從cases變數當中取出一組測試資料。分別用a、b 、expected 三個變數接收,然後我們把這三個變數名作為函式的引數傳遞到 test_add 當中,就實現了引數化的過程。

當使用這一種引數化的手段進行測試的時候,測試資料和測試函式是多對一的關係,對於多組測試資料,我們只需要去編寫一個測試函式,極大的提升了程式碼編寫效率。

6、測試報告和外掛

最後我們來說一下測試報告。pytest 當中的測試報告,通常是以外掛的形式生成的,如果你想生成一個html格式的測試報告,可以先安裝 pytest-html 這個外掛。

pipinstallpytest-html

接下來你需要在執行用例的時候,在 pytest 命令後面加上 --html=<測試報告名稱>.html

pytest--html=report.html

當執行完用例以後,你可以在當前目錄下找到一個 report.html 的檔案,開啟就可以檢視測試報告了。

image-20201221181320782

pytest 之所以成為主流,有很多的原因,其中最重要的一個原因是因為其強大的外掛系統。任何一個程式設計師,只要遵循一些簡單的規範,就可以輕易的編寫外掛。後面我們再跟大家深入去研究 pytest 當中的夾具系統,外掛系統和鉤子函式這些特性。