前置條件:
1.文件路徑:
Test_App - - test_abc.py - - pytest.ini
2.pyetst.ini配置文件內容:
[pytest] 命令行參數 addopts = -s 搜索文件名 python_files = test*.py 搜索的類名 python_classes = Test* 搜索的函數名 python_functions = test_*
pytest之fixture
fixture修飾器來標記固定的工廠函數,在其他函數,模塊,類或整個工程調用它時會被激活並優先執行,通常會被用於完成預置處理和重復操作。
方法:fixture(scope="function", params=None, autouse=False, ids=None, name=None) 常用參數: scope:被標記方法的作用域 session>module>class>function function" (default):作用於每個測試方法,每個test都運行一次 "class":作用於整個類,每個class的所有test只運行一次 一個類中可以有多個方法 "module":作用於整個模塊,每個module的所有test只運行一次;每一個.py文件調用一次,該文件內又有多個function和class "session:作用於整個session(慎用),每個session只運行一次;是多個文件調用一次,可以跨.py文件調用,每個.py文件就是module params:(list類型)提供參數數據,供調用標記方法的函數使用;一個可選的參數列表,它將導致多個參數調用fixture功能和所有測試使用它。 autouse:是否自動運行,默認為False不運行,設置為True自動運行;如果True,則為所有測試激活fixture func可以看到它。如果為False則顯示需要參考來激活fixture
ids:每個字符串id的列表,每個字符串對應於params這樣他們就是測試ID的一部分。如果沒有提供ID它們將從params自動生成;是給每一項params參數設置自定義名稱用,意義不大。
name:fixture的名稱。這默認為裝飾函數的名稱。如果fixture在定義它的統一模塊中使用,夾具的功能名稱將被請求夾具的功能arg遮蔽,解決這個問題的一種方法時將裝飾函數命 令"fixture_<fixturename>"然后使用"@pytest.fixture(name='<fixturename>')"。
fixture第一個例子(通過參數引用)
class Test_ABC: @pytest.fixture() def before(self): print("------->before") def test_a(self,before): # ️ test_a方法傳入了被fixture標識的函數,已變量的形式 print("------->test_a") assert 1 if __name__ == '__main__': pytest.main(["-s test_abc.py"]) .
使用多個fixture
如果用例需要用到多個fixture的返回數據,fixture也可以返回一個元祖,list或字典,然后從里面取出對應數據。
import pytest @pytest.fixture() def test1(): a = 'door' b = '123456' print('傳出a,b') return (a, b) def test_2(test1): u = test1[0] p = test1[1] assert u == 'door' assert p == '123456' print('元祖形式正確') if __name__ == '__main__': pytest.main(['-s','test_abc.py'])
當然也可以分成多個fixture,然后在用例中傳多個fixture參數
import pytest @pytest.fixture() def test1(): a = 'door' print('\n傳出a') return a @pytest.fixture() def test2(): b = '123456' print('傳出b') return b def test_3(test1, test2): u = test1 p = test2 assert u == 'door' assert p == '123456' print('傳入多個fixture參數正確') if __name__ == '__main__': pytest.main(['-s','test_abc.py'])
fixture第二個例子(通過函數引用)
import pytest @pytest.fixture() # fixture標記的函數可以應用於測試類外部 def before(): print("------->before") @pytest.mark.usefixtures("before")#1.需要前面標記了before函數,這才可以用,所以需求before函數前面標記@pytest.fixture();2.前面標記了before函數,這不引用的話,執行后不執行before函數 比如在接口測試中有需要先登錄的就可以使用這個用法
class Test_ABC: def setup(self): print("------->setup") def test_a(self): print("------->test_a") assert 1 if __name__ == '__main__': pytest.main(["-s","test_abc.py"])
使用裝飾器@pytest.mark.usefixtures()修飾需要運行的用例
import pytest @pytest.fixture() def test1(): print('\n開始執行function') @pytest.mark.usefixtures('test1') def test_a(): print('---用例a執行---') @pytest.mark.usefixtures('test1') class Test_Case: def test_b(self): print('---用例b執行---') def test_c(self): print('---用例c執行---') if __name__ == '__main__': pytest.main(['-s', 'test_abc.py'])
疊加usefixtures
如果一個方法或者一個class用例想要同時調用多個fixture,可以使用@pytest.mark.usefixture()進行疊加。注意疊加順序,先執行的放底層,后執行的放上層。
import pytest @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 Test_Case: def test_b(self): print('---用例b執行---') def test_c(self): print('---用例c執行---') if __name__ == '__main__': pytest.main(['-s', 'test_abc.py'])
usefixtures與傳fixture區別
如果fixture有返回值,那么usefixture就無法獲取到返回值,這個是裝飾器usefixture與用例直接傳fixture參數的區別。
當fixture需要用到return出來的參數時,只能講參數名稱直接當參數傳入,不需要用到return出來的參數時,兩種方式都可以。
fixture第三個例子(默認設置為運行,作用域是function)
import pytest @pytest.fixture(scope='function',autouse=True) # 作用域設置為function,自動運行 def before(): print("------->before") class Test_ABC: def setup(self): print("------->setup") def test_a(self): print("------->test_a") assert 1 def test_b(self): print("------->test_b") assert 1 if __name__ == '__main__': pytest.main(["-s", "test_abc.py"])
fixture第五個例子(設置作用域為class)
import pytest @pytest.fixture(scope='class',autouse=True) # 作用域設置為class,自動運行 def before(): print("------->before") class Test_ABC: def setup(self): print("------->setup") def test_a(self): print("------->test_a") assert 1 def test_b(self): print("------->test_b") assert 1 if __name__ == '__main__': pytest.main(["-s","test_abc.py"])
fixture第六個例子(返回值)
import pytest @pytest.fixture() def need_data(): return 2 # 返回數字2 class Test_ABC: def test_a(self, need_data): print("------->test_a") assert need_data != 3 # 拿到返回值做一次斷言 if __name__ == '__main__': pytest.main(["-s", "test_abc.py"])
import pytest @pytest.fixture(params=[1, 2, 3]) def need_data(request): # 傳入參數request 系統封裝參數 return request.param # 取列表中單個值,默認的取值方式 class Test_ABC: def test_a(self, need_data): print("------->test_a") assert need_data != 3 # 斷言need_data不等於3 if __name__ == '__main__': pytest.main(["-s","test_abc.py"])
發現結果運行了三次
fixture(設置作用域為module)
import pytest @pytest.fixture(scope='module') def test1(): b = '男' print('傳出了%s, 且在當前py文件下執行一次!!!' % b) return b def test_3(test1): name = '男' print('找到name') assert test1 == name class Test_Case: def test_4(self, test1): sex = '男' print('找到sex') assert test1 == sex if __name__ == '__main__': pytest.main(["-s","test_abc.py"])
fixture(設置作用域為session)
fixture為session級別是可以跨.py模塊調用的,也就是當我們有多個.py文件的用例的時候,如果多個用例只需調用一次fixture,那就可以設置為scope="session",並且寫到conftest.py文件里。
conftest.py文件名稱時固定的,pytest會自動識別該文件。放到項目的根目錄下就可以全局調用了,如果放到某個package下,那就在改package內有效。
import pytest # conftest.py @pytest.fixture(scope='session') def test1(): a='door' print('獲取到%s' % a) return a
import pytest # abc.py def test3(test1): b = 'pen' print('找到b') assert test1 == b if __name__ == '__main__': pytest.main(['-s', 'abc.py'])
import pytest # abc1.py class Test_Case: def test_4(self, test1): a = 'door' print('找到a') assert test1 == a if __name__ == '__main__': pytest.main(['-s', 'abc1.py'])
如果需要同時執行兩個py文件,可以在cmd中在文件py文件所在目錄下執行命令:pytest -s test_abc.py test_abc.py
conftest.py的作用范圍
一個工程下可以建多個conftest.py的文件,一般在工程根目錄下設置的conftest文件起到全局作用。在不同子目錄下也可以放conftest.py的文件,作用范圍只能在改層級以及以下目錄生效。
1.conftest在不同的層級間的作用域不一樣
2.conftest是不能跨模塊調用的(這里沒有使用模塊調用)
fixture自動使用autouse=True
當用例很多的時候,每次都傳這個參數,會很麻煩。fixture里面有個參數autouse,默認是False沒開啟的,可以設置為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了
平常寫自動化用例會寫一些前置的fixture操作,用例需要用到就直接傳該函數的參數名稱就行了。當用例很多的時候,每次都傳這個參數,會比較麻煩。 fixture里面有個參數autouse,默認是Fasle沒開啟的,可以設置為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了 設置autouse=True autouse設置為True,自動調用fixture功能 start設置scope為module級別,在當前.py用例模塊只執行一次,autouse=True自動使用[圖片]open_home設置scope為function級別, 每個用例前都調用一次,自動使用
autouse設置為True,自動調用fixture功能
import pytest @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 Test_Case: def test_b(self): print('---用例b執行---') def test_c(self): print('---用例c執行---') if __name__ == '__main__': pytest.main(['-s', 'test_abc.py'])
結果可以看到scope=class時也作用於class外的函數
fixture 之 params 使用示例
request 是pytest的內置 fixture ,主要用於傳遞參數
Fixture參數之params參數實現參數化:(可以為list和tuple,或者字典列表,字典元祖等)
import pytest def read_yaml(): return ['1','2','3'] @pytest.fixture(params=read_yaml()) def get_param(request): return request.param def test_01(get_param): print('測試用例:'+get_param) if __name__ == '__main__': pytest.main(['-s','test_abc.py'])
執行結果:
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-6.0.2, py-1.9.0, pluggy-0.13.1
rootdir: E:\PycharmProjects\lianxi, configfile: pytest.ini
plugins: forked-1.3.0, html-1.22.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0
collected 3 items
test_abc.py 測試用例:1
.測試用例:2
.測試用例:3
.
------ generated html file: file://E:\PycharmProjects\lianxi\report.html ------
============================== 3 passed in 0.13s ==============================
注意:
1.此例中test01方法被執行了三次,分別使用的數據為'1','2','3',此結果類似於ddt數據驅動的功能。特別注意:這里的request參數名是固定的,然后request.param的param沒有s。
2.可以把return request.param改成yield request.param,yield也是返回的意思,它和return的區別在於return返回后后面不能接代碼,但是yield返回后,后面還可以接代碼。