本文節選自霍格沃玆測試學院内部教材,文末連結進階學習.
簡介
pytest 是一個成熟的全功能 Python 測試工具,可以幫助您編寫更好的程式。它與 Python 自帶的 Unittest 測試架構類似,但 pytest 使用起來更簡潔和高效,并且相容 unittest 架構。pytest 有以下實用特性:
- pytest 能夠支援簡單的單元測試和複雜的功能測試;
- pytest 本身支援單元測試;
- 可以結合 Requests 實作接口測試;
- 結合 Selenium、Appium 實作自動化功能測試;
- 使用 pytest 結合 Allure 內建到 Jenkins 中可以實作持續內建。工作中一般會使用持續內建來完成代碼內建到主幹分支之後的回歸測試,通過自動化測試的手段來實作産品的快速疊代,同時還能保證産品的高品質。
- pytest 支援 315 種以上的插件;
參考網站:
http://plugincompat.herokuapp.com/ https://docs.pytest.org/安裝
pip install -U pytest
檢視版本
pytest --version
用例的識别與運作
用例編寫規範:
- 測試檔案以 test_ 開頭(以 _test 結尾也可以)
- 測試類以 Test 開頭,并且不能帶有 init 方法
- 測試函數以 test_ 開頭
- 斷言使用基本的 assert 即可
建立一個 python 檔案,命名以 test_ 開頭(或者以 test 結尾),建立測試方法以 test 開頭,測試類需要以 Test 開頭。建立檔案名為 test_add.py 檔案,代碼如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def add(x, y):
return x + y
def test_add():
assert add(1, 10) == 11
assert add(1, 1) == 2
assert add(1, 99) == 100
class TestClass:
def test_one(self):
x = "this"
assert "h" in x
def test_two(self):
x = "hello"
assert hasattr(x, "check")
運作 test_add.py 檔案,在指令行進入到這個檔案所在的路徑,可以直接使用 pytest 指令運作,pytest 會找目前目錄以及遞查找子目錄下所有的 test_.py 或 _test.py 的檔案,把其當作測試檔案。在這些檔案裡,pytest 會收集符合編寫規範的函數,類以及方法,當作測試用例并且執行,執行如下:
$ pytest
....
test_add.py ..F [100%]
....
self = <test_cases.test_add.TestClass object at 0x1091810d0>
def test_two(self):
x = "hello"
> assert hasattr(x, "check")
E AssertionError: assert False
E + where False = hasattr('hello', 'check')
test_add.py:18: AssertionError
===================================================== 1 failed, 2 passed in 0.05s
...
結果分析:執行結果中,F代表用例未通過(斷言錯誤),.用例通過。如果有報錯會有詳細的錯誤資訊。pytest 也支援 Unittest 模式的用例定義。
運作參數
pytest 帶有很多參數,可以使用 pytest --help 來檢視幫助文檔,下面介紹幾種常用的參數:
無參數
讀取路徑下所有符合規則的檔案,類,方法,函數全部執行。使用方法如下:
pytest 或者 py.test
-v 參數
列印詳細運作日志資訊,一般在調試的時候加上這個參數,終端會列印出每條用例的詳細日志資訊,友善定位問題。使用方法如下:
pytest -v
-s 參數
帶控制台輸出結果,當你的代碼裡面有 print 輸出語句,如果想在運作結果中列印 print 輸出的代碼,在運作的時候可以添加 -s 參數,一般在調試的時候使用,使用方法如下:
pytest -s
-k 參數
跳過運作某個或者某些用例。
應用場景:在測試場景中,開發人員有一部分功能代碼還沒實作,測試人員已經将測試用例設計出來,或者測試人員發現了某功能上的 bug 需要開發人員修複之後再測試這部分有缺陷的測試用例,可以将這部分測試用例在運作的時候暫時跳過,等功能實作或者 bug 解決之後再加入運作。
使用方法如下:
pytest -k '類名'
pytest -k '方法名'
pytest -k '類名 and not 方法名' //運作類裡所有的方法,不包含某個方法
-x 參數
遇到用例失敗立即停止運作。
應用場景:在回歸測試過程中,假如一共有10條基礎用例,當開發人員打完包送出測試的時候,需要先運作這10條基礎用例,全部通過才能送出給測試人員正式測試。如果有一條用例失敗,都将這個版本打回給開發人員。這時就可以添加 -x 參數,一旦發現有失敗的用例即中止運作。
pytest -x
--maxfail 參數
用例失敗個數達到閥值停止運作。具體用法:
pytest --maxfail=[num]
應用場景:在回歸測試過程中,假如一共有10條基礎用例,當開發人員打完包送出測試的時候,需要先運作這10條基礎用例,全部通過才能送出給測試人員正式測試。如果運作過程中有 [num] 條用例失敗,即中止運作,後面測試用例都放棄執行,直接退出。這時可以使用 --maxfail 參數。
-m 參數
将運作有 @pytest.mark.[标記名] 這個标記的測試用例。
應用場景:在自動化測試過程中可以将測試用例添加标簽進行分類,比如登入功能、搜尋功能、購物車功能、訂單結算功能等,在運作的時候可以隻運作某個功能的所有的測試用例,比如這個版本隻想驗證登入功能,那就在所有登入功能的測試用例方法上面加上裝飾符 @pytest.mark.login ,運作的時候使用指令添加一個 -m 參數,例如執行 pytest -m login 指令就可以隻執行登入功能這部分的測試用例。
pytest -m [标記名]
運作模式
pytest 提供了多種運作模式,讓開發和調試更得心應手。指定某個子產品,執行單獨一個 pytest 子產品。
應用場景:在編寫測試用例的時候,經常會單獨調試某個類,或者某個方法,這時可以使用 Pycharm 裡面自帶的調試方式,點選用例方法名前面的綠色按鈕,也可以使用指令行的方式單獨運作某個用例。
pytest 中可以使用 pytest 檔案名.py 單獨執行某個 Python 檔案,也可以使用 pytest 檔案名.py::類名 單獨執行某個檔案中的類,使用 pytest 檔案名.py::類名::方法名 單獨執行類中的某個方法。
pytest 檔案名.py
pytest 檔案名.py::類名
pytest 檔案名.py::類名::方法名
在 Pycharm 中運作 pytest 用例

打開 Pycharm -> 設定 -> Tools -> Python Integrated Tools -> Testing: pytest
首先次設定成 pytest ,需要安裝 pytest,可以直接按照這個頁面的提示點選“fix”,也可以在 Project interpreter 裡面添加 pytest 依賴包。安裝完 pytest 之後,編寫的符合規則的測試用例都能被識别出來并且标出一個綠色的執行按鈕,點選這個按鈕也能執行某個方法或者某個類。例如:
Pycharm 設定運作方式為 pytest 之後,用例左側會顯示綠色按鈕,可以直接點選這個按鈕來執行這條用例。
pytest 架構結構
與 unittest 類似,執行用例前後會執行 setup,teardown 來增加用例的前置和後置條件。pytest 架構中使用 setup,teardown 更靈活,按照用例運作級别可以分為以下幾類:
- 子產品級(setup_module/teardown_module)在子產品始末調用
- 函數級(setup_function/teardown_function)在函數始末調用(在類外部)
- 類級(setup_class/teardown_class)在類始末調用(在類中)
- 方法級(setup_method/teardown_methond)在方法始末調用(在類中)
- 方法級(setup/teardown)在方法始末調用(在類中)
調用順序:
setup_module > setup_class >setup_method > setup > teardown > teardown_method > teardown_class > teardown_module
驗證上面的執行順序,看下面的案例。
建立檔案名為 test_run_step.py ,代碼如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def setup_module():
print("\nsetup_module,隻執行一次,當有多個測試類的時候使用")
def teardown_module():
print("\nteardown_module,隻執行一次,當有多個測試類的時候使用")
class TestPytest1(object):
@classmethod
def setup_class(cls):
print("\nsetup_class1,隻執行一次")
@classmethod
def teardown_class(cls):
print("\nteardown_class1,隻執行一次")
def setup_method(self):
print("\nsetup_method1,每個測試方法都執行一次")
def teardown_method(self):
print("teardown_method1,每個測試方法都執行一次")
def test_three(self):
print("test_three,測試用例")
def test_four(self):
print("test_four,測試用例")
class TestPytest2(object):
@classmethod
def setup_class(cls):
print("\nsetup_class2,隻執行一次")
@classmethod
def teardown_class(cls):
print("\nteardown_class2,隻執行一次")
def setup_method(self):
print("\nsetup_method2,每個測試方法都執行一次")
def teardown_method(self):
print("teardown_method2,每個測試方法都執行一次")
def test_two(self):
print("test_two,測試用例")
def test_one(self):
print("test_one,測試用例")
上面的代碼執行完成後,檢視測試結果來分析執行測試順序:
...
plugins: html-2.0.1, rerunfailures-8.0, xdist-1.31.0, ordering-0.6, forked-1.1.3, allure-pytest-2.8.11, metadata-1.8.0
collecting ... collected 4 items
test_run.py::TestPytest1::test_three
setup_module,隻執行一次,當有多個測試類的時候使用
setup_class1,隻執行一次
setup_method1,每個測試方法都執行一次 PASSED [ 25%]test_three,測試用例
teardown_method1,每個測試方法都執行一次
test_run.py::TestPytest1::test_four
setup_method1,每個測試方法都執行一次 PASSED [ 50%]test_four,測試用例
teardown_method1,每個測試方法都執行一次
teardown_class1,隻執行一次
test_run.py::TestPytest2::test_two
setup_class2,隻執行一次
setup_method2,每個測試方法都執行一次 PASSED [ 75%]test_two,測試用例
teardown_method2,每個測試方法都執行一次
test_run.py::TestPytest2::test_one
setup_method2,每個測試方法都執行一次 PASSED [100%]test_one,測試用例
teardown_method2,每個測試方法都執行一次
teardown_class2,隻執行一次
teardown_module,隻執行一次,當有多個測試類的時候使用
============================== 4 passed in 0.03s ===============================
...
從上面的結果可以看出 setup_module 和 teardown_module 在整個子產品隻執行一次,setup_class 和 teardown_class 在類裡面隻執行一次,setup_method 和 teardown_method 在每個方法前後都會調用。
控制用例的執行順序
pytest 加載所有的測試用例是亂序的,如果想指定用例的順序,可以使用 pytest-order 插件,指定用例的執行順序隻需要在測試用例的方法前面加上裝飾器 @pytest.mark.run(order=[num]) 設定order的對應的num值,它就可以按照 num 的大小順序來執行。
應用場景:有時運作測試用例需要指定它的順序,比如有些場景需要先運作完登入,才能執行後續的流程比如購物流程,下單流程,這時就需要指定測試用例的順序。通過 pytest-ordering 這個插件可以完成用例順序的指定。
pip install pytest-ordering
案例
建立一個測試檔案“test_order.py”,代碼如下:
import pytest
class TestPytest(object):
@pytest.mark.run(order=-1)
def test_two(self):
print("test_two,測試用例")
@pytest.mark.run(order=3)
def test_one(self):
print("test_one,測試用例")
@pytest.mark.run(order=1)
def test_three(self):
print("\ntest_three,測試用例")
執行結果,如下檢視執行順序:
省略...
plugins: html-2.0.1, rerunfailures-8.0, xdist-1.31.0, \
ordering-0.6, forked-1.1.3, allure-pytest-2.8.11, metadata-1.8.0
collecting ... collected 3 items
test_order.py::TestPytest::test_three
test_order.py::TestPytest::test_one
test_order.py::TestPytest::test_two
省略...
從上面的執行結果可以看出,執行時以 order 的順序執行:order=1,order=3,order=-1。