Pytest(單元測試框架)
一 安裝
1.1 安裝包安裝
1進入下載包路徑
2.python setup install
3 安裝出現許可權問題:
3.1.mac/linux 新增sudo,執行:sudo python setup install
3.2.windows 管理員方式執行cmd視窗,執行:python setup install
1.2 命令列安裝
1.mac/linux:sudo pip3 install -U pytest # -U:可以理解為--upgrade,表示已安裝就升級為最新版本
2.管理員方式執行cmd:pip3 install -U pytest
1.3 安裝成功校驗:
1.進入命令列
2.執行:pytest --version # 會展示當前已安裝版本
1.4 Pytest執行方式
1.測試類主函式模式
pytest.main("-s test_abc.py")
2.命令列模式
pytest 檔案路徑/測試檔名
例如:
pytest ./test_abc.py
1.5 例子
import pytest # 引入pytest包 def test_a(): # test開頭的測試函式 print("------->test_a") assert 1 # 斷言成功 def test_b(): print("------->test_b") assert 0 # 斷言失敗 if __name__ == '__main__': pytest.main("-s test_abc.py") # 呼叫pytest的main函式執行測試
執行結果:
test_abc.py
------->test_a
. # .(代表成功)
------->test_b
F # F(代表失敗)
二 Pytest的setup和teardown函式
1 概述
1.setup和teardown主要分為:模組級,類級,功能級,函式級。
2.存在於測試類內部
2 函式級別setup()/teardown()
運行於測試方法的始末,即:執行一次測試函式會執行一次setup和teardown
程式碼示例:
import pytest
class Test_ABC:
# 函式級開始
def setup(self):
print("------->setup_method")
# 函式級結束
def teardown(self):
print("------,->teardown_method")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->setup_method # 第一次 setup()
------->test_a
.
------->teardown_method # 第一次 teardown()
------->setup_method # 第二次 setup()
------->test_b
.
------->teardown_method # 第二次 teardown()
3 類級別
運行於測試類的始末,即:在一個測試內只執行一次setup_class和teardown_class,不關心測試類內有多少個測試函式。
程式碼示例:
import pytest
class Test_ABC:
# 測試類級開始
def setup_class(self):
print("------->setup_class")
# 測試類級結束
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->setup_class # 第一次 setup_class()
------->test_a
.
------->test_b
F
------->teardown_class # 第一次 teardown_class()
三 配置檔案
pytest的配置檔案通常放在測試目錄下,名稱為pytest.ini,命令列執行時會使用該配置檔案中的配置.
1 配置pytest命令列執行引數
[pytest]
addopts = -s ... # 空格分隔,可新增多個命令列引數 -所有引數均為外掛包的引數
2 配置測試搜尋的路徑
testpaths = ./scripts # 當前目錄下的scripts資料夾 -可自定義
3 配置測試搜尋的檔名
python_files = test_*.py
# 當前目錄下的scripts資料夾下,以test_開頭,以.py結尾的所有檔案 -可自定義
4 配置測試搜尋的測試類名
python_classes = Test_*
# 當前目錄下的scripts資料夾下,以test_開頭,以.py結尾的所有檔案中,以Test_開頭的類 -可自定義
5 配置測試搜尋的測試函式名
[pytest]
python_functions = test_*
# 當前目錄下的scripts資料夾下,以test_開頭,以.py結尾的所有檔案中,以Test_開頭的類內,以test_開頭的方法 -可自定義
四 常用外掛
前置條件:
1.檔案路徑:
- Test_App
- - pytest.ini
2.pyetst.ini配置檔案內容:
[pytest]
# 命令列引數
addopts = -s
# 搜尋檔名
python_files = test_*.py
# 搜尋的類名
python_classes = Test_*
# 搜尋的函式名
python_functions = test_*
1 Pytest測試報告
通過命令列方式,生成xml/html格式的測試報告,儲存於使用者指定路徑。
外掛名稱:pytest-html
安裝方式:
1.安裝包方式 python setup.py install
2.命令列 pip3 install pytest-html
使用方法:
命令列格式:pytest --html=使用者路徑/report.html
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 0 # 斷言失敗
執行方式:
1.修改Test_App/pytest.ini檔案,新增報告引數,即:addopts = -s --html=./report.html
# -s:輸出程式執行資訊
# --html=./report.html 在當前目錄下生成report.html檔案
⚠️ 若要生成xml檔案,可將--html=./report.html 改成 --html=./report.xml
2.命令列進入Test_App目錄
3.執行命令: pytest
執行結果:
1.在當前目錄會生成assets資料夾和report.html檔案
2 Pytest控制函式執行順序
函式修飾符的方式標記被測試函式執行的順序.
外掛名稱:pytest-ordering
安裝方式:
1.安裝包方式 python setup.py install
2.命令列 pip3 install pytest-ordering
使用方法:
1.標記於被測試函式,@pytest.mark.run(order=x)
2.根據order傳入的引數來解決執行順序
3.order值全為正數或全為負數時,執行順序:值越小,優先順序越高
4.正數和負數同時存在:正數優先順序高
預設情況下,pytest是根據測試方法名由小到大執行的,可以通過第三方外掛包改變其執行順序。
預設執行方式
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 0
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->setup_class
------->test_a # 預設第一個執行
.
------->test_b # 預設第二個執行
F
------->teardown_class
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
@pytest.mark.run(order=2)
def test_a(self):
print("------->test_a")
assert 1
@pytest.mark.run(order=1)
def test_b(self):
print("------->test_b")
assert 0
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->setup_class
------->test_b # order=1 優先執行
F
------->test_a # order=2 晚於 order=1 執行
.
------->teardown_class
3 Pytest失敗重試
通過命令列方式,控制失敗函式的重試次數。
外掛名稱:pytest-rerunfailures
安裝方式:
1.安裝包方式 python setup.py install
2.命令列 pip3 install pytest-rerunfailures
使用方法:
命令列格式:pytest --reruns n # n:為重試的次數
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 0 # 斷言失敗
執行方式:
1.修改Test_App/pytest.ini檔案,新增失敗重試引數,即:addopts = -s --reruns 2 --html=./report.html
# -s:輸出程式執行資訊
# --reruns 2 :失敗測試函式重試兩次
# --html=./report.html 在當前目錄下生成report.html檔案
2.命令列進入Test_App目錄
3.執行命令: pytest
執行結果:
1.在測試報告中可以看到兩次重試記錄
五 用法(一)
前置條件:
1.檔案路徑:
- Test_App
- - pytest.ini
2.pyetst.ini配置檔案內容:
[pytest]
# 命令列引數
addopts = -s
# 搜尋檔名
python_files = test_*.py
# 搜尋的類名
python_classes = Test_*
# 搜尋的函式名
python_functions = test_*
1 pytest之fixture
fixture修飾器來標記固定的工廠函式,在其他函式,模組,類或整個工程呼叫它時會被啟用並優先執行,
通常會被用於完成預置處理和重複操作。
方法:fixture(scope="function", params=None, autouse=False, ids=None, name=None)
常用引數:
scope:被標記方法的作用域
function" (default):作用於每個測試方法,每個test都執行一次
"class":作用於整個類,每個class的所有test只執行一次
"module":作用於整個模組,每個module的所有test只執行一次
"session:作用於整個session(慎用),每個session只執行一次
params:(list型別)提供引數資料,供呼叫標記方法的函式使用
autouse:是否自動執行,預設為False不執行,設定為True自動執行
2 fixture第一個例子(通過引數引用)
示例:
import pytest
class Test_ABC:
@pytest.fixture()
def before(self):
print("------->before")
def test_a(self,before): # ⚠️ test_a方法傳入了被fixture標識的函式,已變數的形式
print("------->test_a")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->before # 發現before會優先於測試函式執行
------->test_a
.
3.fixture第二個例子(通過函式引用)
示例:
import pytest
@pytest.fixture() # fixture標記的函式可以應用於測試類外部
def before():
print("------->before")
@pytest.mark.usefixtures("before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->before # 發現before會優先於測試類執行
------->setup
------->test_a
.
4.fixture第三個例子(預設設定為執行)
示例:
import pytest
@pytest.fixture(autouse=True) # 設定為預設執行
def before():
print("------->before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->before # 發現before自動優先於測試類執行
------->setup
------->test_a
.
5.fixture第四個例子(設定作用域為function)
示例:
import pytest
@pytest.fixture(scope='function',autouse=True) # 作用域設定為function,自動執行
def before():
print("------->before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->before # 執行第一次
------->setup
------->test_a
.------->before # 執行第二次
------->setup
------->test_b
.
6.fixture第五個例子(設定作用域為class)
示例:
import pytest
@pytest.fixture(scope='class',autouse=True) # 作用域設定為class,自動執行
def before():
print("------->before")
class Test_ABC:
def setup(self):
print("------->setup")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 1
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->before # 發現只執行一次
------->setup
------->test_a
.
------->setup
------->test_b
.
7.fixture第六個例子(返回值)
示例一:
import pytest
@pytest.fixture()
def need_data():
return 2 # 返回數字2
class Test_ABC:
def test_a(self,need_data):
print("------->test_a")
assert need_data != 3 # 拿到返回值做一次斷言
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
------->test_a
.
示例二:
import pytest
@pytest.fixture(params=[1, 2, 3])
def need_data(request): # 傳入引數request 系統封裝引數
return request.param # 取列表中單個值,預設的取值方式
class Test_ABC:
def test_a(self,need_data):
print("------->test_a")
assert need_data != 3 # 斷言need_data不等於3
if __name__ == '__main__':
pytest.main("-s test_abc.py")
執行結果:
# 可以發現結果運行了三次
1
------->test_a
.
2
------->test_a
.
3
------->test_a
F
六 用法(二)
1.1 跳過測試函式
根據特定的條件,不執行標識的測試函式.
方法:
skipif(condition, reason=None)
引數:
condition:跳過的條件,必傳引數
reason:標註原因,必傳引數
使用方法:
@pytest.mark.skipif(condition, reason="xxx")
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
@pytest.mark.skipif(condition=2>1,reason = "跳過該函式") # 跳過測試函式test_b
def test_b(self):
print("------->test_b")
assert 0
執行結果:
------->setup_class
------->test_a #只執行了函式test_a
.
------->teardown_class
s # 跳過函式
1.2 標記為預期失敗函式
標記測試函式為失敗函式
方法:
xfail(condition=None, reason=None, raises=None, run=True, strict=False)
常用引數:
condition:預期失敗的條件,必傳引數
reason:失敗的原因,必傳引數
使用方法:
@pytest.mark.xfail(condition, reason="xx")
示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
@pytest.mark.xfail(2 > 1, reason="標註為預期失敗") # 標記為預期失敗函式test_b
def test_b(self):
print("------->test_b")
assert 0
執行結果:
------->setup_class
------->test_a
.
------->test_b
------->teardown_class
x # 失敗標記
2.函式資料引數化
方便測試函式對測試屬性的獲取。
方法:
parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
常用引數:
argnames:引數名
argvalues:引數對應值,型別必須為list
當引數個數為一個時, 格式:[value]
當引數個數大於一個時,格式為:[(param_value1,param_value2.....),(param_value1,param_value2.....)]
使用方法:
@pytest.mark.parametrize(argnames,argvalues)
⚠️ 引數值為N個,測試方法就會執行N次
單個引數示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
@pytest.mark.parametrize("a",[3,6]) # a引數被賦予兩個值,函式會執行兩遍
def test_a(self,a): # 引數必須和parametrize裡面的引數一致
print("test data:a=%d"%a)
assert a%3 == 0
執行結果:
------->setup_class
test data:a=3 # 執行第一次取值a=3
.
test data:a=6 # 執行第二次取值a=6
.
------->teardown_class
多個引數示例:
import pytest
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
@pytest.mark.parametrize("a,b",[(1,2),(0,3)]) # 引數a,b均被賦予兩個值,函式會執行兩遍
def test_a(self,a,b): # 引數必須和parametrize裡面的引數一致
print("test data:a=%d,b=%d"%(a,b))
assert a+b == 3
執行結果:
------->setup_class
test data:a=1,b=2 # 執行第一次取值 a=1,b=2
.
test data:a=0,b=3 # 執行第二次取值 a=0,b=3
.
------->teardown_class
函式返回值型別示例:
import pytest
def return_test_data():
return [(1,2),(0,3)]
class Test_ABC:
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
@pytest.mark.parametrize("a,b",return_test_data()) # 使用函式返回值的形式傳入引數值
def test_a(self,a,b):
print("test data:a=%d,b=%d"%(a,b))
assert a+b == 3
執行結果:
test_abc.py
------->setup_class
test data:a=1,b=2 # 執行第一次取值 a=1,b=2
.
test data:a=0,b=3 # 執行第二次取值 a=0,b=3
.
------->teardown_class