場景
1.一般公司測試環境都有多套,測試的時候我們需要在不同的環境下進行
2.在自動化執行時,在不同的環境下我們要指定不同的系統配置,每次修改框架代碼配置這個很不自動化
3.pytest_addoption注冊參數 這個就很好的解決了這個問題,它能在執行命令的時候傳遞參數
二、@pytest.fixture()函數的介紹
2.1 pytest.fixture()函數介紹
fixture是pytest的核心功能,也是亮點功能;
fixture的目的是提供一個固定基線,在該基線上測試可以可靠地和重復地執行。fixture提供了區別於傳統單元測試(setup/teardown)有顯著改進:
(1)有獨立的命名,並通過聲明它們從測試函數、模塊、類或整個項目中的使用來激活;
(2)按模塊化的方式實現,每個fixture都可以相互調用;
(3)fixture的范圍從簡單的單元擴展到復雜的功能測試,允許根據配置和組件選項對fixture和測試用例進行參數化,或者跨函數function,類class,模塊module或整個測試會話session范圍。
Fixture參數詳解及使用
@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None) 參數詳解: 1、SCOPE 用於控制Fixture的作用范圍 作用類似於Pytest的setup/teardown 默認取值為function(函數級別),控制范圍的排序為:session > module > class > function
|
作用范圍舉例: scope = “function” 語法: @pytest.fixture() #或者 @pytest.fixture(scope='function')
場景一:做為參數傳入
import pytest # fixture函數(類中) 作為多個參數傳入 @pytest.fixture() def login(): print("打開瀏覽器") a = "account" return a @pytest.fixture() def logout(): print("關閉瀏覽器") class TestLogin: #傳入lonin fixture def test_001(self, login): print("001傳入了loging fixture") assert login == "account" #傳入logout fixture def test_002(self, logout): print("002傳入了logout fixture") def test_003(self, login, logout): print("003傳入了兩個fixture") def test_004(self): print("004未傳入仍何fixture哦") if __name__ == '__main__': pytest.main()
運行pytest命令結果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 4 items test_fixture1.py [100%] ============================== 4 passed in 0.03s ==============================11打開瀏覽器 .001傳入了loging fixture 關閉瀏覽器 .002傳入了logout fixture 11打開瀏覽器 關閉瀏覽器 .003傳入了兩個fixture .004未傳入仍何fixture哦 Process finished with exit code 0
場景二、Fixture的相互調用
import pytest # fixtrue作為參數,互相調用傳入 @pytest.fixture() def account(): a = "account" print("第一層fixture") return a #Fixture的相互調用一定是要在測試類里調用這層fixture才會生次,普通函數單獨調用是不生效的 @pytest.fixture() def login(account): print("第二層fixture") class TestLogin: def test_1(self, login): print("直接使用第二層fixture,返回值為{}".format(login)) def test_2(self, account): print("只調用account fixture,返回值為{}".format(account)) if __name__ == '__main__': pytest.main()
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 2 items test_fixture1.py [100%] ============================== 2 passed in 0.03s ==============================第一層fixture 第二層fixture .直接使用第二層fixture,返回值為None 第一層fixture .只調用account fixture,返回值為account Process finished with exit code 0
2.scope = “class”:【@pytest.fixture(scope='class')】
*當測試類內的每一個測試方法都調用了fixture,fixture只在該class下所有測試用例執行前執行一次
**測試類下面只有一些測試方法使用了fixture函數名,這樣的話,fixture只在該class下第一個使用fixture函數的測試用例位置開始算,后面所有的測試用例執行前只執行一次。而該位置之前的測試用例就不管。
語法
場景一、
import pytest # fixture作用域 scope = 'class' @pytest.fixture(scope='class') def login(): print("scope為class") class TestLogin: def test_1(self, login): print("用例1") def test_2(self, login): print("用例2") def test_3(self, login): print("用例3") if __name__ == '__main__': pytest.main()
結果
============================== 3 passed in 0.03s ==============================scope為class .用例1 .用例2 .用例3 Process finished with exit code 0
場景二、
import pytest @pytest.fixture(scope='class') def login(): a = '123' print("輸入賬號密碼登陸") class TestLogin: def test_1(self): print("用例1") def test_2(self): print("用例2") def test_3(self, login): print("用例3") def test_4(self): print("用例4") if __name__ == '__main__': pytest.main()
結果
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 4 items test_fixture1.py .用例1 .用例2 輸入賬號密碼登陸 .用例3 .用例4 [100%] ============================== 4 passed in 0.03s ============================== Process finished with exit code 0
scope = “module”:與class相同,只從.py文件開始引用fixture的位置生效
scope = “session”:用法將在conftest.py文章內詳細介紹
session的作用范圍是針對.py級別的,module是對當前.py生效,seesion是對多個.py文件生效
session只作用於一個.py文件時,作用相當於module
所以session多數與contest.py文件一起使用,做為全局Fixture
2、params:
Fixture的可選形參列表,支持列表傳入 默認None,每個param的值 fixture都會去調用執行一次,類似for循環 可與參數ids一起使用,作為每個參數的標識,詳見ids 被Fixture裝飾的函數要調用是采用:Request.param(固定寫法,如下圖)
import pytest @pytest.fixture(params=[1, 2, {'a': 1, 'b': 2}, {'A': 1, 'B': 2}]) def demo(request): return request.param def test_demo(demo): print("列表值:{}".format(demo)) if __name__ == '__main__': pytest.main()
結果
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 4 items test_fixture1.py [100%] ============================== 4 passed in 0.03s ==============================.列表值:1 .列表值:2 .列表值:{'a': 1, 'b': 2} .列表值:{'A': 1, 'B': 2} Process finished with exit code 0
''' request 是 pytest的內置fixture ''' import pytest # 測試數據 test_data = ["user1", "user2"] @pytest.fixture(params=test_data) def register_users(request): # 獲取當前的測試數據 user = request.param print("setup前置函數拿着這個賬號去注冊:%s" % user) result = "success" return user, result def test_register(register_users): user, result = register_users print("在測試用例里面里面獲取到當前測試數據:%s" % user) print(result) assert result == "success" @pytest.fixture(autouse=True) def show_request(request): print("\n=======================request start=================================") print("request.module==", request.module) print("request.functione==", request.function) print("request.cls==", request.cls) print("request.fspath==", request.fspath) print("request.fixturenames==", request.fixturenames) print("request.fixturename==", request.fixturename) print("request.scope==", request.scope) print("\n=======================request end=================================") if __name__ == '__main__': print(11)
3、ids:
用例標識ID
與params配合使用,一對一關系
舉個栗子:
未配置ids之前,用例:
import pytest @pytest.fixture(params=[1, 2, {'a': 1, 'b': 2}, {'A': 1, 'B': 2}],ids=["one","two","three","four"]) def demo(request): return request.param def test_demo(demo): print("列表值:{}".format(demo)) if __name__ == '__main__': pytest.main()
#使用前后的區別
5、Name:
fixture的重命名 通常來說使用 fixture 的測試函數會將 fixture 的函數名作為參數傳遞,但是 pytest 也允許將fixture重命名 如果使用了name,那只能將name傳如,函數名不再生效 調用方法:@pytest.mark.usefixtures(‘fixture1’,‘fixture2’)
結果
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 2 items test_fixture1.py .使用name參數后,傳入重命名函數,執行成功 E test setup failed file E:\FYR\python\111\test_fixture1.py, line 17 def test_2(test_name): E fixture 'test_name' not found
2.2 fixture作為參數傳入
定義fixture跟定義普通函數差不多,唯一區別就是在函數上加個裝飾器@pytest.fixture(),fixture命名不要用test_開頭,跟用例區分開。用例才是test_開頭的命名。
fixture是可以有返回值的,如果沒return默認返回None。用例調用fixture的返回值,直接就是吧fixture的函數名稱當成變量名稱,
import pytest @pytest.fixture() def user(): print("獲取用戶名") a = "admin" return a def test_1(user): print("a==", user) assert user == "admin" if __name__ == "__main__": pytest.main(["-s", "test_fixture1.py"])
結果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0 collected 1 item test_fixture1.py 獲取用戶名 a== admin . ============================== 1 passed in 0.16s ============================== Process finished with exit code 0
2.3error和failed區別
測試結果一般有三種:passed、failed、error。(skip的用例除外)
如果在test_用例里面斷言失敗,那就是failed
import pytest @pytest.fixture() def user(): print("獲取用戶名") a = "admin" return a def test_1(user): assert user == "admin111" if __name__ == "__main__": pytest.main(["-s", "test_fixture1.py"])
結果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0 collected 1 item test_fixture1.py 獲取用戶名 F ================================== FAILURES =================================== ___________________________________ test_1 ____________________________________ user = 'admin' def test_1(user): > assert user == "admin111" E AssertionError: assert 'admin' == 'admin111' E - admin111 E ? --- E + admin test_fixture1.py:11: AssertionError =========================== short test summary info =========================== FAILED test_fixture1.py::test_1 - AssertionError: assert 'admin' == 'admin111' ============================== 1 failed in 0.15s ============================== Process finished with exit code 0
如果在fixture里面斷言失敗了,那就是error
import pytest @pytest.fixture() def user(): print("獲取用戶名") a = "admin" assert a == "admin123" return a def test_1(user): assert user=="admin" if __name__ == "__main__": pytest.main(["-s", "test_fixture1.py"])
結果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0 collected 1 item test_fixture1.py 獲取用戶名 E =================================== ERRORS ==================================== __________________________ ERROR at setup of test_1 ___________________________ @pytest.fixture() def user(): print("獲取用戶名") a = "admin" > assert a == "admin123" E AssertionError: assert 'admin' == 'admin123' E - admin123 E ? --- E + admin test_fixture1.py:8: AssertionError =========================== short test summary info =========================== ERROR test_fixture1.py::test_1 - AssertionError: assert 'admin' == 'admin123' ============================== 1 error in 0.16s =============================== Process finished with exit code 0
四、request.config.getoption介紹
import pytest def pytest_addoption(parser): parser.addoption("--name", action="store", default="zhangsan", help="my option: name") parser.addoption("--tel", action="store", default="18266669999", help="my option: tel") @pytest.fixture(scope='function') # 根據類型,顯示作用范圍 def start_settings(request): # 獲取--name name = request.config.getoption("--name") # 返回自定義變量的值 tel = request.config.getoption("--tel") # 返回自定義變量的值 yield name, tel # 返回給測試用例使用 def test_case1(start_settings): name, tel = start_settings print("name===", name) print("tel===", tel)