Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
如果你想快速上手pytest,只關(guān)注"Pytest必會(huì)知識(shí)點(diǎn)"章節(jié)就可以了(該章節(jié)已經(jīng)能夠解決基礎(chǔ)的ui和接口自動(dòng)化測(cè)試需求);
如果你想要詳細(xì)了解關(guān)于Fixture的使用方法,請(qǐng)關(guān)注"pytest高級(jí)用法"部分。
pytest 是 python 的第三方單元測(cè)試框架,比unittest 更簡(jiǎn)潔和高效,支持315種以上的插件,同時(shí)兼容 nose、unittest 框架。
官網(wǎng)https://docs.pytest.org/en/latest/contents.html
安裝 pip install pytest
命名規(guī)則如下:
1. 測(cè)試文件應(yīng)當(dāng)命名為 test_<something>.py或者<somethins>_test.py
2. 測(cè)試函數(shù)、測(cè)試類方法應(yīng)當(dāng)命名為test_<something>
3. 測(cè)試類應(yīng)當(dāng)命名為Test<Something>.
1)模塊級(jí)(setup_module/teardown_module)開(kāi)始于模塊始末
2)類級(jí)(setup_class/teardown_class)開(kāi)始于類的始末
3)類里面的(setup/teardown)(運(yùn)行在調(diào)用函數(shù)的前后)
4)功能級(jí)(setup_function/teardown_function)開(kāi)始于功能函數(shù)始末(不在類中)
5)方法級(jí)(setup_method/teardown_method)開(kāi)始于方法始末(在類中)
pytest的mark主要用來(lái)標(biāo)記用例,通過(guò)不同的標(biāo)記實(shí)現(xiàn)不同的運(yùn)行策略
主要用途:
標(biāo)記和分類用例: @pytest.mark.level1
標(biāo)記用例執(zhí)行順順序pytest.mark.run(order=1) (需安裝pytest-ordering)
標(biāo)記用例在指定條件下跳過(guò)或直接失敗 @pytest.mark.skipif()/xfail()
標(biāo)記使用指定fixture(測(cè)試準(zhǔn)備及清理方法) @pytest.mark.usefixtures()
參數(shù)化 @pytest.mark.parametrize
標(biāo)記超時(shí)時(shí)間 @pytest.mark.timeout(60) (需安裝pytest-timeout)
標(biāo)記失敗重跑次數(shù)@pytest.mark.flaky(reruns=5, reruns_delay=1) (需安裝pytest-rerunfailures)
pytest 運(yùn)行目錄下的所有用例
pytest test_reg.py運(yùn)行指定模塊中的所有用例
pytest test_reg.py::TestClass::test_method 運(yùn)行指定模塊指定類指定用例
pytest -m tag 運(yùn)行包含指定標(biāo)簽的所有用例
pytest -k "test_a and test_b" 運(yùn)行名稱包含指定表達(dá)式的用例 (支持and or not)
其他常用參數(shù)
-q: 安靜模式, 不輸出環(huán)境信息
-v: 豐富信息模式, 輸出更詳細(xì)的用例執(zhí)行信息
-s: 顯示程序中的print/logging輸出
pytest的插件也是通過(guò)pip install 命令進(jìn)行安裝,常用的插件如下
pytest-rerunfailures 用例重新執(zhí)行
pytest-html 生成html報(bào)告
pytest-timeout 限制超時(shí)時(shí)間
pytest-parallel 多線程使用,常用配置命令如下:
–workers (optional) *:多進(jìn)程運(yùn)行需要加此參數(shù), *是進(jìn)程數(shù)。默認(rèn)為1。
–tests-per-worker (optional) *:多線程運(yùn)行, *是每個(gè)worker運(yùn)行的最大并發(fā)線程數(shù)。默認(rèn)為1
定義兩個(gè)py文件test_demo1.py和test_demo2.py并放入同一包中
#test_demo1.py
import pytest
import time
class TestPrint:
def setup(self):
print("setup")
@pytest.mark.smoke
def test_test1(self):
time.sleep(2)
assert (1==1)
def test_test2(self):
time.sleep (2)
assert (1==2)
@pytest.mark.smoke
def test_test3(self):
time.sleep (2)
assert (1==1)
def teardown(self):
print ("teardown")
if __name__=='__main__':
pytest.main(['-s', 'demo1.py'])
#test_demo2.py
import pytest
import time
class TestPrint:
def setup(self):
print("setup")
def test_test11(self):
time.sleep(2)
assert (1==1)
@pytest.mark.smoke
def test_test21(self):
time.sleep (2)
assert (1==2)
def test_test31(self):
time.sleep (2)
assert (1==1)
def teardown(self):
print ("teardown")
if __name__=='__main__':
pytest.main(['-s', 'demo2.py'])
在包中運(yùn)行腳本
pytest -v --tests-per-worker 2 --html=report.html
可以看到兩個(gè)py文件中的用例全部被執(zhí)行,并在該目錄下生成了report.html測(cè)試報(bào)告,測(cè)試結(jié)果如下:
============================================================================
test session starts============================================================================
platform win32 -- Python 3.7.4, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 -- c:\python37\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.7.4', 'Platform': 'Windows-10-10.0.17763-SP0', 'Packages': {'pytest': '5.3.2', 'py': '1.8.1', 'pluggy': '0.13.1'}, 'Plugins': {'forked': '1.1.3', 'html': '2.0.1', 'metadata': '1.8.0', 'parallel': '0.0.10', 'rerunfailures': '8.0', '
xdist': '1.31.0'}, 'JAVA_HOME': 'C:\Program Files\Java\jdk1.8.0_151'}
rootdir: C:\Users\Kevin\NewLesson\pytest_demo2
plugins: forked-1.1.3, html-2.0.1, metadata-1.8.0, parallel-0.0.10, rerunfailures-8.0, xdist-1.31.0
collected 6 items
pytest-parallel: 1 worker (process), 2 tests per worker (threads)
test_demo1.py::TestPrint::test_test1 test_demo1.py::TestPrint::test_test2
test_demo1.py::TestPrint::test_test1 PASSED
test_demo1.py::TestPrint::test_test3
test_demo1.py::TestPrint::test_test2 FAILED
test_demo2.py::TestPrint::test_test11
test_demo1.py::TestPrint::test_test3 PASSED
test_demo2.py::TestPrint::test_test21
test_demo2.py::TestPrint::test_test11 PASSED
test_demo2.py::TestPrint::test_test31
test_demo2.py::TestPrint::test_test21 FAILED
test_demo2.py::TestPrint::test_test31 PASSED
===========================================================================FAILURES===========================================================================
________________________________________________________________________________ TestPrint.test_test2 ________________________________________________________________________________
self=<pytest_demo2.test_demo1.TestPrint object at 0x000002B81DFBDEC8>
def test_test2(self):
time.sleep (2)
> assert (1==2)
E assert 1==2
E -1
E +2
test_demo1.py:16: AssertionError
------------------------------------------------------------------------------------------------------------------- Captured stdout setup ------------------------------------------------------------------------------------------------------------------
setupsetup
------------------------------------------------------------------------------------------------------------------ Captured stdout teardown ------------------------------------------------------------------------------------------------------------------
teardown
______________________________________________________________________________
TestPrint.test_test21 ______________________________________________________________________________
self=<pytest_demo2.test_demo2.TestPrint object at 0x000002B81E02B108>
@pytest.mark.smoke
def test_test21(self):
time.sleep (2)
> assert (1==2)
E assert 1==2
E -1
E +2
test_demo2.py:16: AssertionError
------------------------------------------------------------------------------------------------------------------- Captured stdout setup --------------------------------------------------------------------------------------------------------------------
setup
------------------------------------------------------------------------------------------------------------------ Captured stdout teardown ------------------------------------------------------------------------------------------------------------------
teardown
==============================================================================warnings summary==============================================================================
c:\python37\lib\site-packages\_pytest\mark\structures.py:327
c:\python37\lib\site-packages\_pytest\mark\structures.py:327: PytestUnknownMarkWarning: Unknown pytest.mark.smoke - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,
-- Docs: https://docs.pytest.org/en/latest/warnings.html
--------------------------------------------------------------------------------------- generated html file: file://C:\Users\Kevin\NewLesson\pytest_demo2\report.html ----------------------------------------------------------------------------------------
=============================================================================2 failed, 4 passed, 1 warning in 6.42s=============================================================================
備注: 這里主要引用 https://www.cnblogs.com/linuxchao/p/linuxchao-pytest-fixture.html
1.做測(cè)試前后的初始化設(shè)置,如測(cè)試數(shù)據(jù)準(zhǔn)備,鏈接數(shù)據(jù)庫(kù),打開(kāi)瀏覽器等這些操作都可以使用fixture來(lái)實(shí)現(xiàn)
2.測(cè)試用例的前置條件可以使用fixture實(shí)現(xiàn)
3.支持經(jīng)典的xunit fixture ,像unittest使用的setup和teardown
fixture區(qū)別于unnitest的傳統(tǒng)單元測(cè)試(setup/teardown)有顯著改進(jìn):
· 有獨(dú)立的命名,并通過(guò)聲明它們從測(cè)試函數(shù)、模塊、類或整個(gè)項(xiàng)目中的使用來(lái)激活。
· 按模塊化的方式實(shí)現(xiàn),每個(gè)fixture都可以互相調(diào)用。
· fixture的范圍從簡(jiǎn)單的單元測(cè)試到復(fù)雜的功能測(cè)試,可以對(duì)fixture配置參數(shù),或者跨函數(shù)function,類class,模塊module或整個(gè)測(cè)試session范圍。
· fixture可以實(shí)現(xiàn)unittest不能實(shí)現(xiàn)的功能,比如unittest中的測(cè)試用例和測(cè)試用例之間是無(wú)法傳遞參數(shù)和數(shù)據(jù)的,但是fixture卻可以解決這個(gè)問(wèn)題
fixture的名字直接作為測(cè)試用例的參數(shù),上面的實(shí)例就這這種方式,再來(lái)看一個(gè)實(shí)例
# test_fixture.py
import pytest
@pytest.fixture()
def fixtureFunc():
return 'fixtureFunc'
def test_fixture(fixtureFunc):
print('我調(diào)用了{(lán)}'.format(fixtureFunc))
class TestFixture(object):
def test_fixture_class(self, fixtureFunc):
print('在類中使用fixture "{}"'.format(fixtureFunc))
if __name__=='__main__':
pytest.main(['-v', 'test_fixture.py'])
每個(gè)函數(shù)或者類前使用@pytest.mark.usefixtures('fixture')裝飾器裝飾
實(shí)例
# test_fixture.py
import pytest
@pytest.fixture()
def fixtureFunc():
print('\n fixture->fixtureFunc')
@pytest.mark.usefixtures('fixtureFunc')
def test_fixture():
print('in test_fixture')
@pytest.mark.usefixtures('fixtureFunc')
class TestFixture(object):
def test_fixture_class(self):
print('in class with text_fixture_class')
if __name__=='__main__':
pytest.main(['-v', 'test_fixture.py'])
指定fixture的參數(shù)autouse=True這樣每個(gè)測(cè)試用例會(huì)自動(dòng)調(diào)用fixture(其實(shí)這里說(shuō)的不是很準(zhǔn)確,因?yàn)檫€涉及到fixture的作用范圍,那么我們這里默認(rèn)是函數(shù)級(jí)別的,后面會(huì)具體說(shuō)fixture的作用范圍)
實(shí)例
# test_fixture.py
import pytest
@pytest.fixture(autouse=True)
def fixtureFunc():
print('\n fixture->fixtureFunc')
def test_fixture():
print('in test_fixture')
class TestFixture(object):
def test_fixture_class(self):
print('in class with text_fixture_class')
if __name__=='__main__':
pytest.main(['-v', 'test_fixture.py'])
結(jié)果
fixture->fixtureFunc
.in test_fixture
fixture->fixtureFunc
.in class with text_fixture_class
[100%]
==========================2 passed in 0.04 seconds===========================
從結(jié)果可以看到每個(gè)測(cè)試用例執(zhí)行前都自動(dòng)執(zhí)行了fixture
上面所有的實(shí)例默認(rèn)都是函數(shù)級(jí)別的,所以測(cè)試函數(shù)只要調(diào)用了fixture,那么在測(cè)試函數(shù)執(zhí)行前都會(huì)先指定fixture。說(shuō)到作用范圍就不得不說(shuō)fixture 的第二個(gè)參數(shù)scope參數(shù)。
scope參數(shù)可以是session, module,class,function; 默認(rèn)為function
1.session 會(huì)話級(jí)別(通常這個(gè)級(jí)別會(huì)結(jié)合conftest.py文件使用,所以后面說(shuō)到conftest.py文件的時(shí)候再說(shuō))
2.module 模塊級(jí)別: 模塊里所有的用例執(zhí)行前執(zhí)行一次module級(jí)別的fixture
3.class 類級(jí)別 :每個(gè)類執(zhí)行前都會(huì)執(zhí)行一次class級(jí)別的fixture
4.function :前面實(shí)例已經(jīng)說(shuō)了,這個(gè)默認(rèn)是默認(rèn)的模式,函數(shù)級(jí)別的,每個(gè)測(cè)試用例執(zhí)行前都會(huì)執(zhí)行一次function級(jí)別的fixture
下面我們通過(guò)一個(gè)實(shí)例具體看一下 fixture的作用范圍
# test_fixture.py
import pytest
@pytest.fixture(scope='module', autouse=True)
def module_fixture():
print('\n-----------------')
print('我是module fixture')
print('-----------------')
@pytest.fixture(scope='class')
def class_fixture():
print('\n-----------------')
print('我是class fixture')
print('-------------------')
@pytest.fixture(scope='function', autouse=True)
def func_fixture():
print('\n-----------------')
print('我是function fixture')
print('-------------------')
def test_1():
print('\n 我是test1')
@pytest.mark.usefixtures('class_fixture')
class TestFixture1(object):
def test_2(self):
print('\n我是class1里面的test2')
def test_3(self):
print('\n我是class1里面的test3')
@pytest.mark.usefixtures('class_fixture')
class TestFixture2(object):
def test_4(self):
print('\n我是class2里面的test4')
def test_5(self):
print('\n我是class2里面的test5')
if __name__=='__main__':
pytest.main(['-v', '--setup-show', 'test_fixture.py'])
其實(shí)前面的所有實(shí)例都只是做了測(cè)試用例執(zhí)行之前的準(zhǔn)備工作,那么用例執(zhí)行之后該如何實(shí)現(xiàn)環(huán)境的清理工作呢?這不得不說(shuō)yield關(guān)鍵字了,相比大家都或多或少的知道這個(gè)關(guān)鍵字,他的作用其實(shí)和return差不多,也能夠返回?cái)?shù)據(jù)給調(diào)用者,唯一的不同是被掉函數(shù)執(zhí)行遇到y(tǒng)ield會(huì)停止執(zhí)行,接著執(zhí)行調(diào)用處的函數(shù),調(diào)用出的函數(shù)執(zhí)行完后會(huì)繼續(xù)執(zhí)行yield關(guān)鍵后面的代碼(具體原理可以看下我)??聪孪旅娴膶?shí)例來(lái)了解一下如何實(shí)現(xiàn)teardown功能
import pytest
from selenium import webdriver
import time
@pytest.fixture()
def fixtureFunc(): '''實(shí)現(xiàn)瀏覽器的打開(kāi)和關(guān)閉'''
driver=webdriver.Firefox()
yield driver
driver.quit()
def test_search(fixtureFunc):
'''訪問(wèn)百度首頁(yè),搜索pytest字符串是否在頁(yè)面源碼中'''
driver=fixtureFunc
driver.get('http://www.baidu.com')
driver.find_element_by_id('kw').send_keys('pytest')
driver.find_element_by_id('su').click()
time.sleep(3)
source=driver.page_source
assert 'pytest' in source
if __name__=='__main__':
pytest.main(['--setup-show', 'test_fixture.py'])
這個(gè)實(shí)例會(huì)先打開(kāi)瀏覽器,然后執(zhí)行測(cè)試用例,最后關(guān)閉瀏覽器。大家可以試試! 通過(guò)yield就實(shí)現(xiàn)了 用例執(zhí)行后的teardown功能
1、可以跨.py文件調(diào)用,有多個(gè).py文件調(diào)用時(shí),可讓conftest.py只調(diào)用了一次fixture,或調(diào)用多次fixture
2、conftest.py與運(yùn)行的用例要在同一個(gè)pakage下,并且有__init__.py文件
3、不需要import導(dǎo)入 conftest.py,pytest用例會(huì)自動(dòng)識(shí)別該文件,放到項(xiàng)目的根目錄下就可以全局目錄調(diào)用了,如果放到某個(gè)package下,那就在改package內(nèi)有效,可有多個(gè)conftest.py
4、conftest.py配置腳本名稱是固定的,不能改名稱
5、conftest.py文件不能被其他文件導(dǎo)入
6、所有同目錄測(cè)試文件運(yùn)行前都會(huì)執(zhí)行conftest.py文件
主要用途之一共享變量,代碼如下:
import pytest
# conftest.py
@pytest.fixture(scope='session')
def get_token():
token='abcd'
return token
這樣,在多個(gè)測(cè)試用例中使用fixture get_token()時(shí)就可以共享 token值了
元測(cè)試的概念
單元測(cè)試工具
不同的編程語(yǔ)言都有比較成熟的單元測(cè)試框架,語(yǔ)法規(guī)則有些差別,其核心思想都是相通的。常見(jiàn)的單
元測(cè)試框架有:
Java語(yǔ)言:Junit、TestNG
Python語(yǔ)言:UnitTest、Pytest
UnitTest單元測(cè)試框架
UnitTest是Python自帶的一個(gè)單元測(cè)試框架,用它來(lái)做單元測(cè)試。也經(jīng)常應(yīng)用到UI自動(dòng)化測(cè)試和接口自
動(dòng)化測(cè)試中,用來(lái)管理和維護(hù)測(cè)試用例腳本
使用UnitTest框架的好處:
1. 能夠組織多個(gè)用例去執(zhí)行(可以把多條測(cè)試用例封裝成一個(gè)測(cè)試套件,實(shí)現(xiàn)批量執(zhí)行測(cè)試用例)
2. 提供了豐富的斷言方法,方便對(duì)用例執(zhí)行的結(jié)果進(jìn)行判斷
3. 能夠生成HTML格式的測(cè)試報(bào)告
4. 使用Fixture功能可以減少代碼的冗余
UnitTest核心要素:
TestCase就是表示測(cè)試用例
案例
定義一個(gè)實(shí)現(xiàn)加法操作的函數(shù),并對(duì)該函數(shù)進(jìn)行測(cè)試
如何定義測(cè)試用例
1.導(dǎo)包:importunittest
2.定義測(cè)試類:新建測(cè)試類必須繼承unittest.TestCase
3.定義測(cè)試方法:測(cè)試方法名稱命名必須以test開(kāi)頭
示例代碼:
ytest 框架可以用來(lái)做 系統(tǒng)測(cè)試 的自動(dòng)化, 它的特點(diǎn)有
----------------------------------------------------------------------------------------------
pip install pytest
我們還需要產(chǎn)生測(cè)試報(bào)表,所以要安裝一個(gè)第三方插件 pytest-html ,執(zhí)行如下命令安裝
pip install pytest-html
-----------------------------------------------------------------------------------------
pytest 如何知道你哪些代碼是自動(dòng)化的測(cè)試用例?
官方文檔 給出了 pytest 尋找 測(cè)試項(xiàng) 的 具體規(guī)則
-----------------------------------------------------------------------------------------
首先,我們編寫的測(cè)試用例代碼文件, 必須以 test_ 開(kāi)頭,或者以 _test 結(jié)尾
比如,我們創(chuàng)建一個(gè) 文件名為 test_錯(cuò)誤登錄.py ,放在目錄 autotest\cases\登錄 下面。
其中 autotest 是 我們創(chuàng)建的 自動(dòng)化項(xiàng)目根目錄
class Test_錯(cuò)誤密碼:
def test_C001001(self):
print('\n用例C001001')
assert 1==1
def test_C001002(self):
print('\n用例C001002')
assert 2==2
def test_C001003(self):
print('\n用例C001003')
assert 3==2
如果我們把測(cè)試用例存放在類中, 類名必須以 Test 為前綴的 類 ,用例對(duì)應(yīng)的方法必須以 test 為前綴的方法。
pytest 中用例的檢查點(diǎn) 直接用 Python 的 assert 斷言。
assert 后面的表達(dá)式結(jié)果 為 True ,就是 檢查點(diǎn) 通過(guò),結(jié)果為False ,就是檢查點(diǎn) 不通過(guò)。
運(yùn)行測(cè)試
執(zhí)行測(cè)試非常簡(jiǎn)單,打開(kāi)命令行窗口,進(jìn)入自動(dòng)化項(xiàng)目根目錄(我們這里就是 autotest),執(zhí)行命令程序 pytest 即可
顯示找到3個(gè)測(cè)試項(xiàng),2個(gè)執(zhí)行通過(guò),1個(gè)不通過(guò)。
通過(guò)的用例 是用一個(gè)綠色小點(diǎn)表示, 不通過(guò)的用例用一個(gè)紅色的F表示
并且會(huì)在后面顯示具體不通過(guò)的用例 和不通過(guò)的檢查點(diǎn) 代碼細(xì)節(jié)。
大家可以發(fā)現(xiàn),用例代碼中有些打印語(yǔ)句沒(méi)有顯示出內(nèi)容。
因?yàn)閜ytest 會(huì) 截獲print打印的內(nèi)容。
如果我們希望 顯示測(cè)試代碼中print的內(nèi)容,因?yàn)檫@些打印語(yǔ)句在調(diào)試代碼時(shí)很有用,可以加上命令行參數(shù) -s
如下
pytest -s
如果我們希望得到更詳細(xì)的執(zhí)行信息,包括每個(gè)測(cè)試類、測(cè)試函數(shù)的名字,可以加上參數(shù) -v,這個(gè)參數(shù)可以和 -s 合并為 -sv
如下
pytest -sv
執(zhí)行 pytest 時(shí), 如果命令行沒(méi)有指定目標(biāo)目錄 或者 文件, 它會(huì)自動(dòng)搜索當(dāng)前目錄下所有符合條件的文件、類、函數(shù)。
所以上面,就找到了3個(gè)測(cè)試方法,對(duì)應(yīng)3個(gè)用例。
我們目前 項(xiàng)目根目錄 中 只有一個(gè)cases 目錄用例存放測(cè)試用例, 將來(lái)還會(huì)有其他目錄,比如:
lib目錄存放庫(kù)代碼、cfg目錄存放配置數(shù)據(jù) 等等。
為了防止 pytest 到其他目錄中找測(cè)試用例項(xiàng),執(zhí)行測(cè)試時(shí),我們可以在命令行加上目標(biāo)目錄 cases ,就是這樣
pytest cases
--------------------------------------------------------------------------------------------------------------
前面在安裝pytest,我們也安裝了 pytest-html 插件,這個(gè)插件就是用來(lái)產(chǎn)生測(cè)試報(bào)告的。
要產(chǎn)生報(bào)告,在命令行加上 參數(shù) --html=report.html --self-contained-html ,如下
pytest cases --html=report.html --self-contained-html
這樣就會(huì)產(chǎn)生名為 report.html 的測(cè)試報(bào)告文件,可以在瀏覽器中打開(kāi)
但是這個(gè)工具有個(gè)bug,導(dǎo)致測(cè)試目錄、文件、類名 中,如果有中文,顯示為亂碼
可以這樣修復(fù):
然后再次運(yùn)行,就可以發(fā)現(xiàn)中文亂碼問(wèn)題已經(jīng)解決了。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。