fixture主要用於測試函數傳參和前置后置操作
一,fixture當參數傳入
fixture可以當做參數傳入,定義fixture跟定義普通函數差不多,唯一區別就是在函數上加個裝飾器@pytest.fixture()
fixture命名不要以test開頭,跟用例區分開
fixture是有返回值的,沒有返回值默認為None
用例調用fixture的返回值,直接就是把fixture的函數名稱當做變量名稱。
@pytest.fixture() def creat_id(): a=2 return a*3 def test_compute(creat_id): some=creat_id+4 assert some==11
creat_id = 6 def test_compute(creat_id): some=creat_id+4 > assert some==11 E assert 10 == 11 test_study.py:42: AssertionError
在這里故意用例執行失敗,通過回溯可以看到
1,test_compute測試函數需要一個名為的函數參數creat_id,通過查找名為的帶有fixture標記的函數,可以找到匹配的夾具函數creat_id
2,creat_id() 被稱為創建實例,得到creat_id=6
3,test_compute函數開始執行,斷言失敗
當fixture標記的函數沒有返回值時,執行用例,改fixture標記函數返回值默認為None
creat_id = None def test_compute(creat_id): > some=creat_id+4 E TypeError: unsupported operand type(s) for +: 'NoneType' and 'int' test_study.py:41: TypeError
如果想要實現該creat_id函數能夠被其他模塊/類等共享使用,可以把寫入到conftest.py文件,並寫上scope對應的值,對於可能的值scope有:function,class,module,package或session。不填寫默認function
二,多個fixture使用
1.如果用例需要用到多個fixture的返回數據,fixture也可以返回一個元祖,list或字典,然后從里面取出對應數據。
@pytest.fixture() def creat_id(): return {'a':2*3,'b':2+3} def test_compute(creat_id): some=creat_id['a']+4 some2 = creat_id['b'] + 4 assert some==11 assert some2 == 11
執行用例,可以看到creat_id得到一個字典,然后再分別取值
creat_id = {'a': 6, 'b': 5} def test_compute(creat_id): some=creat_id['a']+4 some2 = creat_id['b'] + 4 > assert some==11 E assert 10 == 11 test_study.py:41: AssertionError
2.也可以分成多個fixture,然后在用例中傳多個fixture參數
@pytest.fixture() def creat_id(): return 2*3 @pytest.fixture() def creat_id2(): return 2+3 def test_compute(creat_id,creat_id2): some=creat_id+4 some2 = creat_id2 + 4 assert some==11 assert some2 == 11
執行用例,可以看到creat_id = 6, creat_id2 = 5
creat_id = 6, creat_id2 = 5 def test_compute(creat_id,creat_id2): some=creat_id+4 some2 = creat_id2 + 4 > assert some==11 E assert 10 == 11 test_study.py:46: AssertionError
三,fixture互相調用
將定義為fixture的方法的返回值用在其他方法中
四,fixture的作用范圍
fixture里面有個scope參數可以控制fixture的作用范圍:
@pytest.fixture(scope='function')
1
session>module>class>function
-function:每一個函數或方法都會調用,默認情況下是function
-class:每一個類調用一次,一個類中可以有多個方法
-module:每一個.py文件調用一次,該文件內又有多個function和class
-session:是多個文件調用一次,可以跨.py文件調用,每個.py文件就是module
注:
fixture為session級別是可以跨.py模塊調用的,也就是當我們有多個.py文件的用例的時候,如果多個用例只需調用一次fixture,那就可以設置為scope=“session”,並且寫到conftest.py文件里。
conftest.py文件名稱時固定的,pytest會自動識別該文件。放到項目的根目錄下就可以全局調用了,如果放到某個package下,那就在該ackage內有效
scope越大,實例化越早
1.當函數調用多個fixtures的時候,scope較大的(比如session)實例化早於scope較小的(比如function或者class)
2.同樣scope的順序則按照其在測試函數中定義的順序及依賴關系來實例化
fixture源碼詳解
fixture(scope=‘function’,params=None,autouse=False,ids=None,name=None):
scope:有四個級別參數"function"(默認),“class”,“module”,“session”
params:一個可選的參數列表,它將導致多個參數調用fixture功能和所有測試使用它。
autouse:如果True,則為所有測試激活fixture func可以看到它。如果為False則顯示需要參考來激活fixture
ids:每個字符串id的列表,每個字符串對應於params這樣他們就是測試ID的一部分。如果沒有提供ID它們將從params自動生成
name:fixture的名稱。這默認為裝飾函數的名稱。如果fixture在定義它的統一模塊中使用,夾具的功能名稱將被請求夾具的功能arg遮蔽,解決這個問題的一種方法時將裝飾函數命令"fixture_“然后使用”@pytest.fixture(name=’’)"。
五,調用fixture的四種方法
1.函數或類里面方法直接傳fixture的函數參數名稱@pytest.fixture()
@pytest.fixture() def test1(): print('\n開始執行function') def test_a(test1): print('---用例a執行---') #執行結果 開始執行function ---用例a執行---
2.使用裝飾器@pytest.mark.usefixtures()修飾需要運行的用例,該方式獲取不到fixture標識函數的返回值
如:@pytest.mark.usefixtures('test1')
@pytest.fixture() def test1(): print('\n開始執行function') @pytest.mark.usefixtures('test1') def test_a(): print('---用例a執行---') #執行結果 開始執行function ---用例a執行---
usefixtures與傳fixture區別
usefixture,無法獲取到返回值。傳fixture,可以獲取到返回值。
所以當fixture需要用到return出來的參數時,只能使用傳fixture方式,當不需要用到return出來的參數時,兩種方式都可以。
3.疊加usefixtures
如果一個方法或者一個class用例想要同時調用多個fixture,可以使用
@pytest.mark.usefixtures()進行疊加。
注意疊加順序,先執行的放底層,后執行的放上層。
如:先執行test2 再執行test1
@pytest.mark.usefixtures('test1') @pytest.mark.usefixtures('test2') def test_xx(): #測試用例 pass
4.autouse設置為True,自動調用fixture功能
設置scope為module級別,在當前.py用例模塊只執行一次,autouse=True自動使用
設置scope為function級別,每個用例前都調用一次,自動使用
如:@pytest.fixture(scope="module", autouse=True)
六,fixture使用yield實現setup和teardown
可以通過使用yield語句代替return,fixture里面的teardown用yield來喚醒teardown的執行,yield語句之后的所有代碼都將用作拆卸代碼
@pytest.fixture(scope="module") def open(): print("\n1,打開瀏覽器,並且打開百度首頁") #實現setup yield 10 #“yield 10”替代“return 10”,且實現teardown print("\n2,執行teardown!") print("3,最后關閉瀏覽器") def test_compute2(open): assert open==10 def test_compute3(open): assert open==9
執行時加上“-s -q”命令,可以發現,scope="module"代表每個模塊調用一次,調用時先執行open中的第一行打印代碼,然后yield 10返回數值9給測試函數使用,當兩個測試函數都執行完畢后(一個成功一個失敗),再執行yield后面的兩行打印代碼
1,打開瀏覽器,並且打開百度首頁 .F 2,執行teardown! 3,最后關閉瀏覽器
yield遇到異常:
1.如果其中一個用例出現異常,不影響yield后面的teardown執行,運行結果互不影響,並且在用例全部執行完之后,會呼喚teardown的內容
2.如果在setup就異常了,那么是不會去執行yield后面的teardown內容了
七,使用帶有參數化fixture
與在@ pytest.mark.parametrize中使用的方式相同,用於在參數化燈具的值集中應用標記。在fixture中使用params參數
是一個可選的參數列表,它將導致多個參數調用fixture功能和所有測試使用它。如:
@pytest.fixture(params=[0, 1)
然后被fixture標記的函數需要使用request中的request.param才能使用到params中的值
1,
@pytest.fixture(params=[3,4,5, pytest.param(2, marks=pytest.mark.skip)]) def creat_id(request): print('打印param的值',request.param) return request.param+3 def test_compute(creat_id): assert creat_id==6
執行時加‘-v’命令,可以看到每次測試函數的情況,
運行此測試將跳過對creat_id函數中value 的調用“2”,最后test_compute一共執行了3次,分別使用了creat_id函數的三個值,每次得到一個參數值;跳過1條
test_study.py::test_compute[3] 打印param的值 3 PASSED test_study.py::test_compute[4] 打印param的值 4 FAILED test_study.py::test_compute[5] 打印param的值 5 FAILED test_study.py::test_compute[2] SKIPPED
六,在各個級別上覆蓋fixture
1.一個工程下可以建多個conftest.py的文件,一般在工程根目錄下設置的conftest文件起到全局作用。在不同子目錄下也可以放conftest.py的文件,作用范圍只能在該層級以及以下目錄生效。
2.conftest在不同的層級間的作用域不一樣,再執行某模塊時,先執行這個模塊對應的祖先目錄是否有conftest文件,然后依次往下級目錄查找
3.conftest是不能跨模塊調用的
4.conftest.py與運行的用例要在同一個pakage下,並且有init.py文件
注意:
Pytest對於每個fixture只會緩存一個實例,這意味着如果使用參數化的fixture,pytest可能會比定義的作用域更多次的調用fixture函數(因為需要創建不同參數的fixture)
可以使用pytest --fixtures xx.py 來查看可用的fixture
在相對較大的測試套件中,很可能需要override一個global或root一個已locally 定義的夾具,以保持測試代碼的可讀性和可維護性。
1,在文件夾級別(通過conftest文件)重寫fixtures方法
如在/test1/conftest.py和/test1/test2/conftest.py中都有一個被fixture標識的同名方法:
則/test1/路徑下的測試模塊使用conftest.py中的的fixture方法,
而/test1/test2/路徑下的測試模塊會使用fixture.py文件中重寫的fixture方法
test1/ __init__.py conftest.py #test1/ conftest.py import pytest @pytest.fixture def username(): return 'username' test_something.py # test1/test_something.py def test_username(username): assert username == 'username' test2/ __init__.py conftest.py # test1/test2/conftest.py import pytest @pytest.fixture def username(username): return 'overridden-' + username test_something.py # test1/test2/test_something.py def test_username(username): assert username == 'overridden-username'
2,在測試模塊級別重寫fixtures方法
如在/test1/conftest.py都有一個被fixture標識的同名方法:username()
/test1路徑下有一測試模塊test_something.py,重寫了fixtures方法1:username()
/test1路徑下有一測試模塊test_something_else.py,重寫了fixtures方法2:username()
這兩個文件分別使用自己的fixtures方法:username()
test1/ __init__.py conftest.py # test1/conftest.py import pytest @pytest.fixture def username(): return 'username' test_something.py # test1/test_something.py import pytest @pytest.fixture def username(username): return 'overridden-' + username def test_username(username): assert username == 'overridden-username' test_something_else.py # test1/test_something_else.py import pytest @pytest.fixture def username(username): return 'overridden-else-' + username def test_username(username): assert username == 'overridden-else-username'
總結:
fixtures方法的查找方法
先從根目錄下的fixture.py文件中查找,
依次往次級目錄的fixture.py文件中查找,
最后再查找當前測試模塊py文件中是否有該fixtures方法,使用最近的一個fixtures方法
注:同一個py文件中可以有同名的fixtures方法,也是使用最近的一個
原文鏈接:https://blog.csdn.net/shuyaoyao/article/details/101103671