一. fixture介紹
fixture是pytest的一個(gè)閃光點(diǎn)议纯,pytest要精通怎么能不學(xué)習(xí)fixture呢?跟著我一起深入學(xué)習(xí)fixture吧耕肩。其實(shí)unittest和nose都支持fixture那槽,但是pytest做得更炫。
fixture是pytest特有的功能鳞芙,它用pytest.fixture標(biāo)識,定義在函數(shù)前面期虾。在你編寫測試函數(shù)的時(shí)候原朝,你可以將此函數(shù)名稱做為傳入?yún)?shù),pytest將會以依賴注入方式镶苞,將該函數(shù)的返回值作為測試函數(shù)的傳入?yún)?shù)喳坠。
fixture有明確的名字,在其他函數(shù)茂蚓,模塊壕鹉,類或整個(gè)工程調(diào)用它時(shí)會被激活。
fixture是基于模塊來執(zhí)行的聋涨,每個(gè)fixture的名字就可以觸發(fā)一個(gè)fixture的函數(shù)晾浴,它自身也可以調(diào)用其他的fixture。
我們可以把fixture看做是資源牍白,在你的測試用例執(zhí)行之前需要去配置這些資源脊凰,執(zhí)行完后需要去釋放資源。比如module類型的fixture茂腥,適合于那些許多測試用例都只需要執(zhí)行一次的操作狸涌。
fixture還提供了參數(shù)化功能切省,根據(jù)配置和不同組件來選擇不同的參數(shù)。
fixture主要的目的是為了提供一種可靠和可重復(fù)性的手段去運(yùn)行那些最基本的測試內(nèi)容帕胆。比如在測試網(wǎng)站的功能時(shí)朝捆,每個(gè)測試用例都要登錄和退出迟几,利用fixture就可以只做一次亲铡,否則每個(gè)測試用例都要做這兩步也是冗余。
下面會反復(fù)提高Python的Module概念捉腥,Python中的一個(gè)Module對應(yīng)的就是一個(gè).py文件歼捐。其中定義的所有函數(shù)或者是變量都屬于這個(gè)Module。這個(gè)Module 對于所有函數(shù)而言就相當(dāng)于一個(gè)全局的命名空間晨汹,而每個(gè)函數(shù)又都有自己局部的命名空間豹储。
二. Fixture基礎(chǔ)實(shí)例入門
把一個(gè)函數(shù)定義為Fixture很簡單,只能在函數(shù)聲明之前加上“@pytest.fixture”淘这。其他函數(shù)要來調(diào)用這個(gè)Fixture剥扣,只用把它當(dāng)做一個(gè)輸入的參數(shù)即可。
test_fixture_basic.py
import pytest
@pytest.fixture()
def before():
print '\nbefore each test'
def test_1(before):
print 'test_1()'
def test_2(before):
print 'test_2()'
assert 0
下面是運(yùn)行結(jié)果铝穷,test_1和test_2運(yùn)行之前都調(diào)用了before钠怯,也就是before執(zhí)行了兩次。默認(rèn)情況下曙聂,fixture是每個(gè)測試用例如果調(diào)用了該fixture就會執(zhí)行一次的晦炊。
C:\Users\yatyang\PycharmProjects\pytest_example>pytest -v -s test_fixture_basic.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0 -- C:\Python27\python.exe
cachedir: .cache
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 2 items
test_fixture_basic.py::test_1
before each test
test_1()
PASSED
test_fixture_basic.py::test_2
before each test
test_2()
FAILED
================================== FAILURES ===================================
___________________________________ test_2 ____________________________________
before = None
def test_2(before):
print 'test_2()'
> assert 0
E assert 0
test_fixture_basic.py:12: AssertionError
===================== 1 failed, 1 passed in 0.23 seconds ======================
如果你的程序出現(xiàn)了下面的錯(cuò)誤,就是開始忘記添加‘import pytest',所以不要忘記羅宁脊。
=================================== ERRORS ====================================
_________________ ERROR collecting test_fixture_decorator.py __________________
test_fixture_decorator.py:2: in <module>
@pytest.fixture()
E NameError: name 'pytest' is not defined
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.36 seconds ===========================
三. 調(diào)用fixture的三種方式
1. 在測試用例中直接調(diào)用它断国,例如第二部分的基礎(chǔ)實(shí)例。
2. 用fixture decorator調(diào)用fixture
可以用以下三種不同的方式來寫榆苞,我只變化了函數(shù)名字和類名字稳衬,內(nèi)容沒有變。第一種是每個(gè)函數(shù)前聲明坐漏,第二種是封裝在類里薄疚,類里的每個(gè)成員函數(shù)聲明,第三種是封裝在類里在前聲明赊琳。在可以看到3中不同方式的運(yùn)行結(jié)果都是一樣街夭。
test_fixture_decorator.py
import pytest
@pytest.fixture()
def before():
print('\nbefore each test')
@pytest.mark.usefixtures("before")
def test_1():
print('test_1()')
@pytest.mark.usefixtures("before")
def test_2():
print('test_2()')
class Test1:
@pytest.mark.usefixtures("before")
def test_3(self):
print('test_1()')
@pytest.mark.usefixtures("before")
def test_4(self):
print('test_2()')
@pytest.mark.usefixtures("before")
class Test2:
def test_5(self):
print('test_1()')
def test_6(self):
print('test_2()')
運(yùn)行結(jié)果如下,和上面的基礎(chǔ)實(shí)例的運(yùn)行效果一樣躏筏。
C:\Users\yatyang\PycharmProjects\pytest_example>pytest -v -s test_fixture_decorator.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0 -- C:\Python27\python.exe
cachedir: .cache
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 6 items
test_fixture_decorator.py::test_1
before each test
test_1()
PASSED
test_fixture_decorator.py::test_2
before each test
test_2()
PASSED
test_fixture_decorator.py::Test1::test_3
before each test
test_1()
PASSED
test_fixture_decorator.py::Test1::test_4
before each test
test_2()
PASSED
test_fixture_decorator.py::Test2::test_5
before each test
test_1()
PASSED
test_fixture_decorator.py::Test2::test_6
before each test
test_2()
PASSED
========================== 6 passed in 0.10 seconds ===========================
3. 用autos調(diào)用fixture
fixture decorator一個(gè)optional的參數(shù)是autouse, 默認(rèn)設(shè)置為False莱坎。
當(dāng)默認(rèn)為False,就可以選擇用上面兩種方式來試用fixture寸士。
當(dāng)設(shè)置為True時(shí)檐什,在一個(gè)session內(nèi)的所有的test都會自動調(diào)用這個(gè)fixture碴卧。
權(quán)限大,責(zé)任也大乃正,所以用該功能時(shí)也要謹(jǐn)慎小心住册。
import time
import pytest
@pytest.fixture(scope="module", autouse=True)
def mod_header(request):
print('\n-----------------')
print('module : %s' % request.module.__name__)
print('-----------------')
@pytest.fixture(scope="function", autouse=True)
def func_header(request):
print('\n-----------------')
print('function : %s' % request.function.__name__)
print('time : %s' % time.asctime())
print('-----------------')
def test_one():
print('in test_one()')
def test_two():
print('in test_two()')
從下面的運(yùn)行結(jié)果,可以看到mod_header在該module內(nèi)運(yùn)行了一次瓮具,而func_header對于每個(gè)test都運(yùn)行了一次荧飞,總共兩次。該方式如果用得好名党,還是可以使代碼更為簡潔叹阔。
但是對于不熟悉自己組的測試框架的人來說,在pytest里面去新寫測試用例传睹,需要去了解是否已有一些fixture是module或者class級別的需要注意耳幢。
C:\Users\yatyang\PycharmProjects\pytest_example>pytest -v -s test_fixture_auto.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0 -- C:\Python27\python.exe
cachedir: .cache
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 2 items
test_fixture_auto.py::test_one
-----------------
module : test_fixture_auto
-----------------
-----------------
function : test_one
time : Sat Mar 18 06:56:54 2017
-----------------
in test_one()
PASSED
test_fixture_auto.py::test_two
-----------------
function : test_two
time : Sat Mar 18 06:56:54 2017
-----------------
in test_two()
PASSED
========================== 2 passed in 0.03 seconds ===========================
四. fixture scope
function:每個(gè)test都運(yùn)行,默認(rèn)是function的scope
class:每個(gè)class的所有test只運(yùn)行一次
module:每個(gè)module的所有test只運(yùn)行一次
session:每個(gè)session只運(yùn)行一次
比如你的所有test都需要連接同一個(gè)數(shù)據(jù)庫欧啤,那可以設(shè)置為module睛藻,只需要連接一次數(shù)據(jù)庫,對于module內(nèi)的所有test邢隧,這樣可以極大的提高運(yùn)行效率店印。
五. fixture 返回值
在上面的例子中,fixture返回值都是默認(rèn)None倒慧,我們可以選擇讓fixture返回我們需要的東西按摘。如果你的fixture需要配置一些數(shù)據(jù),讀個(gè)文件纫谅,或者連接一個(gè)數(shù)據(jù)庫院峡,那么你可以讓fixture返回這些數(shù)據(jù)或資源。
如何帶參數(shù)
fixture還可以帶參數(shù)系宜,可以把參數(shù)賦值給params照激,默認(rèn)是None。對于param里面的每個(gè)值盹牧,fixture都會去調(diào)用執(zhí)行一次俩垃,就像執(zhí)行for循環(huán)一樣把params里的值遍歷一次。
test_fixture_param.py
import pytest
@pytest.fixture(params=[1, 2, 3])
def test_data(request):
return request.param
def test_not_2(test_data):
print('test_data: %s' % test_data)
assert test_data != 2
可以看到test_not_2里面把用test_data里面定義的3個(gè)參數(shù)運(yùn)行里三次汰寓。
C:\Users\yatyang\PycharmProjects\pytest_example>pytest -v -s test_fixture_param.py
============================= test session starts =============================
platform win32 -- Python 2.7.13, pytest-3.0.6, py-1.4.32, pluggy-0.4.0 -- C:\Python27\python.exe
cachedir: .cache
metadata: {'Python': '2.7.13', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'py': '1.4.32', 'pytest': '3.0.6', 'pluggy': '0.4.0'}, 'JAVA_HOME': 'C:\\Program Files (x86)\\Java\\jd
k1.7.0_01', 'Plugins': {'html': '1.14.2', 'metadata': '1.3.0'}}
rootdir: C:\Users\yatyang\PycharmProjects\pytest_example, inifile:
plugins: metadata-1.3.0, html-1.14.2
collected 3 items
test_fixture_param.py::test_not_2[1] test_data: 1
PASSED
test_fixture_param.py::test_not_2[2] test_data: 2
FAILED
test_fixture_param.py::test_not_2[3] test_data: 3
PASSED
================================== FAILURES ===================================
________________________________ test_not_2[2] ________________________________
test_data = 2
def test_not_2(test_data):
print('test_data: %s' % test_data)
> assert test_data != 2
E assert 2 != 2
test_fixture_param.py:9: AssertionError
===================== 1 failed, 2 passed in 0.24 seconds ======================
本文對pytest的fixture進(jìn)行了深入的講解和練習(xí)口柳,希望讀者能夠把fixture這些高級功能都用到平時(shí)的pytest功能中,提高運(yùn)行效率有滑。