一、fixture介紹
fixture是pytest特有的功能,它用pytest.fixture標識,定義在函數前面。在你編寫測試函數的時候,你可以將此函數名稱作為傳入參數,pytest將會以依賴注入方式,將該函數的返回值作為測試函數的傳入參數。
fixture有明確的名字,在其他函數,模塊,類或者整個工程調用它時被激活。
fixture是基於模塊來執行的,每個fixture的名字就可以觸發一個fixture的函數,它自身也可以調用其他的fixture。
我們可以把fixture看作是資源,在你的測試用例執行之前需要去配置這些資源,執行完成后需要去釋放資源。比如module類型的fixture,適合於那些許多測試用例都只需要執行一次的操作。
fixture還提供了參數化功能,根據配置和不同組件來選擇不同的參數。
fixture主要的目的是為了提供一種可靠和可重復性的手段去運行那些最基本的測試內容。比如在測試網站的功能時,每個測試用例都要登陸和退出,利用fixture就可以只做一次,否則每個測試用例都要做這兩步也是冗余。
下面會反復提高python的module概念,python中的一個module對應的就是一個.py文件。其中定義的所有函數或者變量都屬於這個module。這個module對於所有函數而言就相當於一個全局的命名空間,而每個函數又都有自己局部的命名空間。
二、fixture基礎實例入門
把一個函數定義為fixture很簡單,只要在函數聲明之前加上“@pytest.fixture”即可。其他函數要來調用這個fixture,只用把它當作一個輸入的參數即可。
文件名稱:test_fixture_basic.py
1 import pytest 2 3 @pytest.fixture() 4 def before(): 5 print ('nbefore each test') 6 7 def test_1(before): 8 print ('test_1()') 9 10 def test_2(before): 11 print ('test_2()') 12 assert 0
運行結果如下圖:test_1和test_2運行之前都調用了before,也就是before執行了兩次。默認情況下,fixture是每個測試用例如果調用了該fixture就會執行一次的。
三、調用fixture的三種方式
1. 在測試用例中直接調用它,例如第二部分的基礎實例
2. 用fixture裝飾器調用fixture
可以用以下三種不同的方式來寫,我只變化了函數名字和類名字,內容沒有變。第一種是每個函數前聲明,第二種是封裝在類里,類里的每個成員函數聲明,第三種是封裝在類里在函數前聲明。可以看到三種不同方式的運行結果都是一樣的。
文件名稱:test_fixture_decorator.py
1 import pytest 2 3 @pytest.fixture() 4 def before(): 5 print('\nbefore each test') 6 7 @pytest.mark.usefixtures("before") 8 def test_1(): 9 print('test_1()') 10 11 @pytest.mark.usefixtures("before") 12 def test_2(): 13 print('test_2()') 14 15 class Test1: 16 @pytest.mark.usefixtures("before") 17 def test_3(self): 18 print('test_1()') 19 20 @pytest.mark.usefixtures("before") 21 def test_4(self): 22 print('test_2()') 23 24 @pytest.mark.usefixtures("before") 25 class Test2: 26 def test_5(self): 27 print('test_1()') 28 29 def test_6(self): 30 print('test_2()')
運行結果如下圖:和上面的基礎實例運效果一樣。
3. 用autouse調用fixture
fixture裝飾器的一個optional可選的參數autouse,默認設置為False.
1 源代碼如下: 2 def fixture(scope="function", params=None, autouse=False, ids=None, name=None): 3 if callable(scope) and params is None and autouse is False: 4 # direct decoration 5 return FixtureFunctionMarker("function", params, autouse, name=name)(scope) 6 if params is not None and not isinstance(params, (list, tuple)): 7 params = list(params) 8 return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name)
當默認False時就可以用上面兩種方式調用fixture。
當設置True時,在一個session內的所有的test都會自動調用這個fixture。權限大,責任也大,所以用該功能時也要謹慎小心。
1 mport time 2 import pytest 3 4 @pytest.fixture(scope="module", autouse=True) 5 def mod_header(request): 6 print('\n-----------------') 7 print('module : %s' % request.module.__name__) 8 print('-----------------') 9 10 @pytest.fixture(scope="function", autouse=True) 11 def func_header(request): 12 print('\n-----------------') 13 print('function : %s' % request.function.__name__) 14 print('time : %s' % time.asctime()) 15 print('-----------------') 16 17 def test_one(): 18 print('in test_one()') 19 20 def test_two(): 21 print('in test_two()')
但是對於不熟悉自己組的測試框架的人來說,在pytest里面去新寫測試用例,需要去了解是否已有一些fixture是module或者class級別的需要注意。
四、fixture scope
function:每個test都運行,默認是function的scope
class:每個class的所有test只運行一次
module:每個module的所有test只運行一次
session:每個session只運行一次
比如你的所有test都需要連接同一個數據庫,那可以設置為module,只需要連接一次數據庫,對於module內的所有test,這樣可以極大的提高運行效率。
五、fixture返回值
在上面的例子中,fixture返回值都是默認None,我們可以選擇讓fixture返回我們需要的東西。如果你的fixture需要配置一些數據,讀個文件,或者連接一個數據庫,那么你可以讓fixture返回這些數據或資源。
如何帶參數
fixture還可以帶參數,可以把參數賦值給params,默認是None。對於param里面的每個值,fixture都會去調用執行一次,就像執行for循環一樣把params里的值遍歷一次。
test_fixture_param.py
1 import pytest 2 3 @pytest.fixture(params=[1, 2, 3]) 4 def test_data(request): 5 return request.param 6 7 def test_not_2(test_data): 8 print('test_data: %s' % test_data) 9 assert test_data != 2
可以看到test_not_2里面把用test_data里面定義的3個參數運行里三次。