簡介
pytest是python的一種單元測試框架,與python自帶的unittest測試框架類似,但是比unittest框架使用起來更簡潔,效率更高。並且pytest兼容unittest的用例,支持的插件也更多
安裝
pip install pytest
簡單上手,創建個test_sample.py文件
def func(x): return x + 1 def test_answer(): assert func(3) == 5
運行測試,直接在當前文件夾運行pytest
collected 1 item test_sample.py F [100%] ================================= FAILURES ================================= _______________________________ test_answer ________________________________ def test_answer(): > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_sample.py:6: AssertionError ============================ 1 failed in 0.12s =============================
pytest運行規則:查找當前目錄及其子目錄下以test_*.py或*_test.py文件,找到文件后,在文件中找到以test開頭函數並執行。
以類來封裝用例
# content of test_class.py class TestClass: def test_one(self): x = "this" assert "h" in x def test_two(self): x = "hello" assert hasattr(x, "check")
運行可以使用pytest [file_path]指定文件,-q是靜默模式,不會打印用例輸出
$ pytest -q test_class.py .F [100%] ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ self = <test_class.TestClass object at 0xdeadbeef> def test_two(self): x = "hello" > assert hasattr(x, "check") E AssertionError: assert False E + where False = hasattr('hello', 'check') test_class.py:8: AssertionError 1 failed, 1 passed in 0.12s
用例設計原則
- 文件名以test_*.py文件和*_test.py
- 以test_開頭的函數
- 以Test開頭的類
- 以test_開頭的方法
- 所有的包pakege必須要有__init__.py文件
執行用例
1.執行某個目錄下所有的用例
pytest 文件名/
2.執行某一個py文件下用例
pytest 腳本名稱.py
3.-k 按關鍵字匹配
pytest -k "MyClass and not method"
這將運行包含與給定字符串表達式匹配的名稱的測試,其中包括Python使用文件名,類名和函數名作為變量的運算符。 上面的例子將運行TestMyClass.test_something但不運行TestMyClass.test_method_simple
4.按節點運行
每個收集的測試都分配了一個唯一的nodeid,它由模塊文件名和后跟說明符組成來自參數化的類名,函數名和參數,由:: characters分隔。
運行.py模塊里面的某個函數
pytest test_mod.py::test_func
運行.py模塊里面,測試類里面的某個方法
pytest test_mod.py::TestClass::test_method
5.標記表達式
pytest -m slow
將運行用@ pytest.mark.slow裝飾器修飾的所有測試,slow是自己命名的標記,可以自定義
import pytest @pytest.mark.finished def test_send_http(): pass def test_something_quick(): pass
運行測試時使用-m選項可以加上邏輯
>pytest -m "finished and commit" //匹配finished和commit運行 >pytest -m "finished and not merged" //finished運行,merged不運行
6.從包里面運行
pytest --pyargs pkg.testing
這將導入pkg.testing並使用其文件系統位置來查找和運行測試。
7.在第一個(或N個)失敗后停止
pytest -x # stop after first failure pytest --maxfail=2 # stop after two failures
8.跳過測試
使用pytest.mark.skip標記需要跳過的用例
@pytest.mark.skip(reason="not finished") def test_send_http(): pass
也支持使用 pytest.mark.skipif 為測試函數指定被忽略的條件
@pytest.mark.skipif(finishflag==Fasle,reason="not finished") def test_send_http(): pass
9.腳本調用執行
直接使用 pytest.main() 像命令行一樣傳遞參數 pytest.main(["-x", "mytestdir"])
用例編寫
斷言
pytest直接使用python assert語法來寫
def f(): return 3 def test_function(): assert f() == 4
斷言中添加消息
assert a % 2 == 0, "value was odd, should be even"
預設與清理
與unittest中的setup和teardown類似,pytest也有這樣的環境清理方法,主要有
-
模塊級(setup_module/teardown_module)開始於模塊始末,全局的
-
函數級(setup_function/teardown_function)只對函數用例生效(不在類中)
-
類級(setup_class/teardown_class)只在類中前后運行一次(在類中)
-
方法級(setup_method/teardown_method)開始於方法始末(在類中)
-
類里面的(setup/teardown)運行在調用方法的前后
import pytest class TestClass: def setup_class(self): print("setup_class:類中所有用例執行之前") def teardown_class(self): print("teardown_class:類中所有用例執行之前") def setup_method(self): print("setup_method: 每個用例開始前執行") def teardown_method(self): print("teardown_method: 每個用例結束后執行") def setup(self): print("setup: 每個用例開始前執行") def teardown(self): print("teardown: 每個用例結束后執行") def test_one(self): print("執行第一個用例") def test_two(self): print("執行第二個用例") def setup_module(): print("setup_module:整個.py模塊只執行一次") def teardown_module(): print("teardown_module:整個.py模塊只執行一次") def setup_function(): print("setup_function:每個方法用例開始前都會執行") def teardown_function(): print("teardown_function:每個方法用例結束前都會執行") def test_three(): print("執行第三個用例")
使用pytest -s test_sample.py運行,-s參數是為了顯示用例的打印信息,下面是輸出,可以看出幾個方法之間的優先級
test_sample.py setup_module:整個.py模塊只執行一次
setup_class:類中所有用例執行之前
setup_method: 每個用例開始前執行
setup: 每個用例開始前執行
執行第一個用例
.teardown: 每個用例結束后執行
teardown_method: 每個用例結束后執行
setup_method: 每個用例開始前執行
setup: 每個用例開始前執行
執行第二個用例
.teardown: 每個用例結束后執行
teardown_method: 每個用例結束后執行
teardown_class:類中所有用例執行之前
setup_function:每個方法用例開始前都會執行
執行第三個用例
.teardown_function:每個方法用例結束前都會執行
teardown_module:整個.py模塊只執行一次
注意:setup_method和teardown_method的功能和setup/teardown功能是一樣的,一般二者用其中一個即可;函數里面用到的setup_function/teardown_function與類里面的setup_class/teardown_class互不干涉
參數化
使用pytest.mark.parametrize(argnames, argvalues)可以實現函數的參數化
@pytest.mark.parametrize('text',['test1','test2','test3']) def test_one(text): print(text)
argnames就是形參名稱,argvalues就是待測的一組數據
固件fixture
基本使用
固件Fixture是一些函數,pytest 會在執行測試函數之前(或之后)加載運行它們。主要是為一些單獨測試用例需要預先設置與清理的情況下使用的。
不同於上面的setup和teardown的就是,可以自定義函數,可以指定用例運行,使用方法如下
@pytest.fixture() def text(): print("開始執行") #使用pytest.fixture()裝飾一個函數成為fixture def test_one(): print("執行第一個用例") def test_two(text): #用例傳入fixture函數名,以此來確認執行 print("執行第二個用例")
使用yield可以實現固件的拆分運行,yield前在用例前執行,yield后再用例后執行
@pytest.fixture() def text(): print("開始執行") yield #yield 關鍵詞將固件分為兩部分,yield 之前的代碼屬於預處理,會在測試前執行;yield 之后的代碼屬於后處理,將在測試完成后執行 print("執行完畢") def test_one(): print("執行第一個用例") def test_two(text): print("執行第二個用例")
統一管理
固件可以直接定義在各測試腳本中,就像上面的例子。更多時候,我們希望一個固件可以在更大程度上復用,這就需要對固件進行集中管理。Pytest 使用文件 conftest.py 集中管理固件。
不用顯式調用 conftest.py,pytest 會自動調用,可以把 conftest 當做插件來理解
./conftest.py @pytest.fixture() def text(): print("開始執行") yield print("執行完畢") ./test_sample.py def test_one(): print("執行第一個用例") def test_two(text): print("執行第二個用例")
作用域
fixture可以通過 scope 參數聲明作用域,比如
- function: 函數級,每個測試函數都會執行一次固件;
- class: 類級別,每個測試類執行一次,所有方法都可以使用;
- module: 模塊級,每個模塊執行一次,模塊內函數和方法都可使用;
- session: 會話級,一次測試只執行一次,所有被找到的函數和方法都可用。
./conftest.py @pytest.fixture(scope="module") def text(): print("開始執行") yield print("執行完畢") ./test_sample.py def test_one(text): print("執行第一個用例") def test_two(text): print("執行第二個用例")
執行情況
test_sample.py 開始執行
執行第一個用例
.執行第二個用例
.執行完畢
如果對於類使用作用域,需要使用 pytest.mark.usefixtures(對函數和方法也適用)
./conftest.py @pytest.fixture(scope="class") def text(): print("開始執行") yield print("執行完畢") ./test_sample.py @pytest.mark.usefixtures('text') class TestClass: def test_one(self): print("執行第一個用例") def test_two(self): print("執行第二個用例")
自動運行
將fixture的autouse參數設置為True時,可以不用傳入函數,自動運行
./conftest.py @pytest.fixture(scope="module",autouse=True) def text(): print("開始執行") yield print("執行完畢") ./test_sample.py def test_one(): print("執行第一個用例") def test_two(): print("執行第二個用例")
參數化
使用fixture的params參數可以實現參數化
./conftest.py @pytest.fixture(scope="module",params=['test1','test2']) def text(request): print("開始執行") yield request.param print("執行完畢") ./test_sample.py def test_one(text): print("執行第一個用例") print(text) def test_two(text): print("執行第二個用例")
固件參數化需要使用 pytest 內置的固件 request,並通過 request.param 獲取參數。
結果如下
test_sample.py 開始執行
執行第一個用例
test1
.執行第二個用例
.執行完畢
開始執行
執行第一個用例
test2
.執行第二個用例
.執行完畢
生成報告
HTML報告
安裝pytest-html
pip install pytest-html
使用方法是,直接在命令行pytest命令后面加--html=<文件名字或者路徑>.html參數就可以了
pytest --html=report.html
結果如下
上面生成的報告包括html和一個assets文件(里面是報告CSS樣式),如果要合成一個文件可以添加下面的參數
pytest --html=report.html --self-contained-html
XML報告
使用命令可以生成XML格式報告
pytest --junitxml=report.xml
allure報告
1.首先安裝java環境
下載JDK http://www.oracle.com/technetwork/java/javase/downloads/index.html
安裝對應系統的包,如windowsx64是xxx-windows-x64.exe
一路下一步安裝就可以了
然后在環境變量中添加下面變量
變量名:JAVA_HOME 變量值:C:\Program Files (x86)\Java\jdk1.8.0_91 // 要根據自己的實際路徑配置 變量名:CLASSPATH 變量值:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; //記得前面有個"." 變量名:Path 變量值:%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
然后再cmd中運行 java -version、java、javac 幾個命令沒有報錯即可
2.安裝allure
下載allure 會跳轉到github,找到link下載
解壓包后進入bin文件,復制路徑並添加到環境變量Path中
cmd運行allure命令,沒有報錯即正確
3.pytest使用
安裝插件
pip install allure-pytest
如果timeout就加 --index-url https://pypi.douban.com/simple 用豆瓣源
進入測試py文件目錄,運行
pytest --alluredir ./report
運行完成后生成report文件,-alluredir后面跟的是文件路徑,可以自定義
使用allure查看報告,直接啟動allure server后面加報告路徑就行
allure serve report(報告文件夾名)
等一會就生成報告
參考: