python通用測試框架大多數人用的是unittest+HTMLTestRunner,這段時間看到了pytest文檔,發現這個框架和豐富的plugins很好用,所以來學習下pytest.
pytest介紹:
pytest是一個非常成熟的全功能的Python測試框架,主要有以下幾個特點:
- 簡單靈活,容易上手
- 支持參數化
- 能夠支持簡單的單元測試和復雜的功能測試,還可以用來做selenium/appnium等自動化測試、接口自動化測試(pytest+requests)
- pytest具有很多第三方插件,並且可以自定義擴展,比較好用的如pytest-selenium(集成selenium)、pytest-html(完美html測試報告生成)、pytest-rerunfailures(失敗case重復執行)、pytest-xdist(多CPU分發)等
- 測試用例的skip和xfail處理
- 可以很好的和jenkins集成
- report框架----allure 也支持了pytest
####
安裝pytest:
pip install -U pytest
驗證安裝的版本:
pytest --version
####
幾個pytest documentation中的例子:
例子1:
1.創建test_sample.py文件,創建一個方法、一個用例
import pytest # content of test_sample.py def func(x): return x + 1 def test_answer(): assert func(3) == 5
命令行切換到文件所在目錄,執行測試(也可以直接在IDE中運行):
命令:pytest
這個測試返回一個失敗報告,因為func(3)不返回5。
####
例子2:
當需要編寫多個測試樣例的時候,我們可以將其放到一個測試類當中,如:
創建test_sample2.py文件
class TestClass: def test_one(self): x = "this" assert 'h' in x def test_two(self): x = "hello" assert hasattr(x, 'check')
運行以上例子:
pytest -q test_sample2.py
從測試結果中可以看到,該測試共執行了兩個測試樣例,一個失敗一個成功。
同樣,我們也看到失敗樣例的詳細信息,和執行過程中的中間結果。-q即-quiet,作用是減少冗長,具體就是不再展示pytest的版本信息。
####
如何編寫pytest測試樣例
通過上面2個實例,我們發現編寫pytest測試樣例非常簡單,只需要按照下面的規則:
- 測試文件以test_開頭(以_test結尾也可以)
- 測試類以Test開頭,並且不能帶有 init 方法
- 測試函數以test_開頭
- 斷言使用基本的assert即可
運行模式
Pytest的多種運行模式,讓測試和調試變得更加得心應手,下面介紹5種常用的模式。
在介紹之前需要提醒一句,運行pytest時會找當前目錄及其子目錄中的所有test_*.py 或 *_test.py格式的文件以及以test開頭的方法或者class,不然就會提示找不到可以運行的case了。
1.運行后生成測試報告(htmlReport)
安裝pytest-html:
pip install pytest-html
運行模式:
pytest --html=report.html
報告效果還可以
在以上報告中可以清晰的看到測試結果和錯誤原因,定位問題很容易。
2.運行指定的case
當我們寫了較多的cases時,如果每次都要全部運行一遍,無疑是很浪費時間的,通過指定case來運行就很方便了。
例子代碼:
新建test_sample3.py
class TestClassOne(object): def test_one(self): x = "this" assert 't'in x def test_two(self): x = "hello" assert hasattr(x, 'check') class TestClassTwo(object): def test_one(self): x = "iphone" assert 'p'in x def test_two(self): x = "apple" assert hasattr(x, 'check')
運行模式:
模式1:直接運行test_se.py文件中的所有cases:
pytest test_sample3.py
模式2:運行ttest_sample3.py文件中的TestClassOne這個class下的兩個cases:
pytest test_sample3.py::TestClassOne
模式3:運行test_sample3.py文件中的TestClassTwo這個class下的test_one:
pytest test_sample3.py::TestClassTwo::test_one
注意:定義class時,需要以T開頭,不然pytest是不會去運行該class的。
3.多進程運行cases
當cases量很多時,運行時間也會變的很長,如果想縮短腳本運行的時長,就可以用多進程來運行。
安裝pytest-xdist:
pip install -U pytest-xdist
運行模式:
pytest test_se.py -n NUM
其中NUM填寫並發的進程數。
4.重試運行cases
在做接口測試時,有事會遇到503或短時的網絡波動,導致case運行失敗,而這並非是我們期望的結果,此時可以就可以通過重試運行cases的方式來解決。
安裝pytest-rerunfailures:
pip install -U pytest-rerunfailures
運行模式:
pytest test_se.py --reruns NUM
NUM填寫重試的次數。
5.顯示print內容
在運行測試腳本時,為了調試或打印一些內容,我們會在代碼中加一些print內容,但是在運行pytest時,這些內容不會顯示出來。如果帶上-s,就可以顯示了。
運行模式:
pytest test_se.py -s
另外,pytest的多種運行模式是可以疊加執行的,比如說,你想同時運行4個進程,又想打印出print的內容。可以用:
pytest test_se.py -s -n 4
其他運行方法
執行目錄下所有用例
pytest testcase/
####
pytest參數
1、-K EXPRESSION
執行某個關鍵字的用例
用例要匹配給出的表達式;使用python的語法,匹配的范圍是文件名、類名、函數名為變量,用and來區分
運行pytest時帶-k參數
pytest -k "test and TestClass and not test_a" test.py
可以看出,test_a這個用例被取消選擇了,沒有運行了
3、--maxfail=num
當錯誤個數到達給定數時,退出測試,這里就不列舉實例了,結果與-x類似
4、-m MARKEXPR
只能運行有相應標識的測試用例,使用這個參數,測試用例要使用@pytest.mark.marker修飾
# content of test.py import pytest class TestClass(object): def test_one(self): '''new_etests''' x = "this" assert 'h' in x @pytest.mark.slow def test_two(self): '''new_sssetests''' x = "hello" assert hasattr(x, 'check') def test_a(self): assert 1==2
teste_two使用了@pytest.mark.slow來修飾
在使用時,使用如下參數
pytest –m slow test.py
從上圖中可以看出,只運行了一個我們帶有標識的用例。
注意,-m后面不能帶''號(單引號),只能帶“”(雙引號),不然識別不到
如果要運行多個標識的話,用表達式,如下
pytest -m "slow or faster" 運行有slow標識或 faster標識用例
pytest -m "slow and faster" 運行有slow和faster標識的用例
pytest -m "slow and not faster" 運行有slow和沒有faster標識的用例
5、 -v, --verbose
詳細結果
6、-q, --quiet
極簡結果顯示,簡化控制台的輸出,可以看出輸出信息和之前不添加-q不信息不一樣, 下圖中有兩個..點代替了pass結果
7、-s
輸入我們用例中的調式信息,比如print的打印信息等,我們在用例中加上一句 print(driver.title),我們再運行一下我們的用例看看,調試信息輸出
8、-V
可以輸出用例更加詳細的執行信息,比如用例所在的文件及用例名稱等
9、--junit-xml=path
輸出xml文件格式,在與jenkins做集成時使用
10、 --result-log=path
將最后的結果保存到本地文件中
注意:標黃的是經常使用的
####
pytest fixture 這個很重要!
1、fixture scope的范圍參數
之前使用@pytest.fixture(scope='module')來定義框架,scope的參數有以下幾種
function 每一個用例都執行
class 每個類執行
module 每個模塊執行(函數形式的用例)
session 每個session只運行一次,在自動化測試時,登錄步驟可以使用該session
2、調用fixture的三種方法
2.1函數或類里面方法直接傳fixture的函數參數名稱
from __future__ import print_function import pytest @pytest.fixture(scope='module') def resource_a_setup(request): print('\nresources_a_setup()') def resource_a_teardown(): print('\nresources_a_teardown()') request.addfinalizer(resource_a_teardown) def test_1(resource_a_setup): print('test_1()') def test_2(): print('\ntest_2()') def test_3(resource_a_setup): print('\ntest_3()')
運行
pytest -s -v test_sample4.py
這是setup teardown的pytest的用法
2.2使用裝飾器@pytest.mark.usefixtures()修飾需要運行的用例
import pytest # test_fixture1.py @pytest.fixture() def test1(): print('\n開始執行function') @pytest.mark.usefixtures('test1') def test_a(): print('---用例a執行---') @pytest.mark.usefixtures('test1') class TestCase: def test_b(self): print('---用例b執行---') def test_c(self): print('---用例c執行---') if __name__ == '__main__': pytest.main(['-s', 'test_sample5.py'])
運行
pytest -s -v test_sample4.py
這是在每一個用例的前面都加了一下處理
2.3疊加usefixtures
如果一個方法或者一個class用例想要同時調用多個fixture,可以使用@pytest.mark.usefixture()進行疊加。注意疊加順序,先執行的放底層,后執行的放上層
import pytest # test_fixture1.py @pytest.fixture() def test1(): print('\n開始執行function1') @pytest.fixture() def test2(): print('\n開始執行function2') @pytest.mark.usefixtures('test1') @pytest.mark.usefixtures('test2') def test_a(): print('---用例a執行---') @pytest.mark.usefixtures('test2') @pytest.mark.usefixtures('test1') class TestCase: def test_b(self): print('---用例b執行---') def test_c(self): print('---用例c執行---') if __name__ == '__main__': pytest.main(['-s', 'test_fixture1.py'])
3.usefixtures與傳fixture區別
如果fixture有返回值,那么usefixture就無法獲取到返回值,這個是裝飾器usefixture與用例直接傳fixture參數的區別。
當fixture需要用到return出來的參數時,只能講參數名稱直接當參數傳入,不需要用到return出來的參數時,兩種方式都可以。
4.fixture自動使用autouse=True
當用例很多的時候,每次都傳這個參數,會很麻煩。fixture里面有個參數autouse,默認是False沒開啟的,可以設置為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了
autouse設置為True,自動調用fixture功能
import pytest # test_fixture1.py @pytest.fixture(scope='module', autouse=True) def test1(): print('\n開始執行module') @pytest.fixture(scope='class', autouse=True) def test2(): print('\n開始執行class') @pytest.fixture(scope='function', autouse=True) def test3(): print('\n開始執行function') def test_a(): print('---用例a執行---') def test_d(): print('---用例d執行---') class TestCase: def test_b(self): print('---用例b執行---') def test_c(self): print('---用例c執行---') if __name__ == '__main__': pytest.main(['-s', 'test_fixture1.py'])
這樣就不用每一個用例去加裝飾器了,清爽了很多,
5.conftest.py的作用范圍
一個工程下可以建多個conftest.py的文件,一般在工程根目錄下設置的conftest文件起到全局作用。在不同子目錄下也可以放conftest.py的文件,作用范圍只能在改層級以及以下目錄生效。
'''scope=session'''
'''fixture為session級別是可以跨.py模塊調用的,也就是當我們有多個.py文件的用例時候,如果多個用例只需調用一次fixture,那就可以設置為scope="session",並且寫到conftest.py文件里'''
'''conftest.py文件名稱是固定的,pytest會自動識別該文件。放到工程的根目錄下,就可以全局調用了,如果放到某個package包下,那就只在該package內有效'''
'''如果想同時運行test_fixture11.py和test_fixture12.py,在cmd執行
> pytest -s test_fixture11.py test_fixture12.py
'''
5.1conftest在不同的層級間的作用域不一樣
# conftest層級展示/conftest.py import pytest @pytest.fixture(scope='session', autouse=True) def login(): print('----准備登錄----') # conftest層級展示/sougou_login/conftest import pytest @pytest.fixture(scope='session', autouse=True) def bai_du(): print('-----登錄百度頁面-----') # conftest層級展示/sougou_login/login_website import pytest class TestCase: def test_login(self): print('hhh,成功登錄百度') if __name__ == '__main__': pytest.main(['-s', 'login_website.py'])
###
5.2conftest是不能跨模塊調用的(這里沒有使用模塊調用)
# conftest層級演示/log/contfest.py import pytest @pytest.fixture(scope='function', autouse=True) def log_web(): print('打印頁面日志成功') # conftest層級演示/log/log_website.py import pytest def test_web(): print('hhh,成功一次打印日志') def test_web1(): print('hhh,成功兩次打印日志') if __name__ == '__main__': pytest.main(['-s', 'log_website.py'])
###
pytest之mark的使用,這個也很重要
使用方法:
1、注冊標簽名
2、在測試用例/測試類前面加上:@pytest.mark.標簽名
打標記范圍:測試用例、測試類、模塊文件
注冊方式:
1、單個標簽:
在conftest.py添加如下代碼:
def pytest_configure(config): # demo是標簽名 config.addinivalue_line("markers", "demo:示例運行")
2、多個標簽:
在conftest.py添加如下代碼:
def pytest_configure(config): marker_list = ["testdemo", "demo", "smoke"] # 標簽名集合 for markers in marker_list: config.addinivalue_line("markers", markers)
3、添加pytest.ini 配置文件(在你項目的任意一個文件下,新建一個file,文件命名為pytest.ini)
[pytest] markers= smoke:this is a smoke tag demo:demo testdemo:testdemo
使用方法:
import pytest @pytest.mark.testdemo def test_demo01(): print("函數級別的test_demo01") @pytest.mark.smoke def test_demo02(): print("函數級別的test_demo02") @pytest.mark.demo class TestDemo: def test_demo01(self): print("test_demo01") def test_demo02(self): print("test_demo02")
運行方式:
1、命令行模式
通過標記表達式執行 pytest -m demo 這條命令會執行被裝飾器@pytest.mark.demo裝飾的所有測試用例 生成html報告: pytest -m demo --html=Report/report.html 生成xml報告: pytest -m demo --junitxml=Report/report.xml 運行指定模塊: pytest -m demo --html=Report/report.html TestCases/test_pytest.py 運行指定測試目錄 pytest -m demo --html=Report/report.html TestCases/ 通過節點id來運行: pytest TestCases/test_pytest.py::TestDemo::test_demo01 通過關鍵字表達式過濾執行 pytest -k "MyClass and not method" 這條命令會匹配文件名、類名、方法名匹配表達式的用例 獲取用例執行性能數據 獲取最慢的10個用例的執行耗時 pytest --durations=10
2、新建run.py文件運行,代碼如下:
pytest.main(["-m","demo","--html=Report/report.html"])
###
####