1. setUp()和tearDown()
setup()函數主要是進行測試前的初始化工作,比如:在接口測試前面做一些前置的參數賦值,數據庫操作等等。
teardown()函數是測試后的清除工作,比如:參數還原或銷毀,數據庫的還原恢復等。
總結:
setup()函數表示測試類中每個測試方法執行前都需要執行的操作,
teardown()函數表示每個測試方法執行后都需要執行的操作。
說明每個級別的含義:
模塊級:指的是一個.py文件。
類級:一個.py文件中可以寫多個類。(一般情況下只寫一個類)
方法級:類中定義的方法叫方法。
函數級:類外定義的方法叫函數。
Pytest框架setUp()函數和tearDown()函數主要分為:模塊級,類級,方法級,函數級。
Pytest框架提供的setUp()函數和tearDown()函數如下:
模塊級與函數級,不定義在測試類中。
模塊級:setup_module()/teardown_module():開始于模塊始末,全局的。
函數級:setup_function()/teardown_function():只對函數用例生效(不在類中)。
類級與方法級,定義在類中
類級:setup_class()/teardown_class():只在類中前后運行一次(在類中)。
方法級:setup_method()/teardown_method():開始于方法始末(在類中)。
自由的:setup()/teardown():直接使用感覺和方法級前后置函數一樣。
示例
(1)方法級
"""
test_01.py
setup_method()和 teardown_method()函數
需要定義在測試類中,定義在類外不起作用。
setup_method()定義場景,如:打開瀏覽器,加載網頁等
teardown_method()場景,如:關閉瀏覽器等
"""
import pytest# 測試類
class Test_setUp_tearDown:# 方法級,前置函數def setup_method(self):print("setup_method(self):在每個測試方法之前執行")# 方法級,后置函數def teardown_method(self):print("teardown_method(self):在每個測試方法之后執行\n")# 測試用例adef test_a(self):print("test_a方法")assert True# 測試用例bdef test_b(self):print("test_b方法")assert Trueif __name__ == '__main__':pytest.main()"""
執行結果:
test_a setup_method(self):在每個測試方法之前執行
PASSED [ 50%]test_a方法
teardown_method(self):在每個測試方法之后執行test_b setup_method(self):在每個測試方法之前執行
PASSED [100%]test_b方法
teardown_method(self):在每個測試方法之后執行
"""
(2)類級
"""
test_02.py
setup_class()和 teardown_class()函數
需要定義在測試類中,定義在類外不起作用。
setup_class()定義場景,比如:創建日志對象,創建數據庫的連接,創建接口的請求對象等。
teardown_class()定義場景,比如:銷毀日志對象,銷毀數據庫的連接,銷毀接口的請求對象。
"""
import pytestclass Test_setUp_tearDown:# 方法級,前置函數def setup_method(self):print("setup_method(self):在每個測試方法之前執行")# 方法級,后置函數def teardown_method(self):print("teardown_method(self):在每個測試方法之后執行\n")# 類級,前置函數def setup_class(self):print("setup_class(self):每個測試類之前執行一次\n")# 類級,后置函數def teardown_class(self):print("teardown_class(self):每個測試類之后執行一次")# 測試用例adef test_a(self):print("test_a方法")assert True# 測試用例bdef test_b(self):print("test_b方法")assert Trueif __name__ == '__main__':pytest.main()
"""
執行結果:
setup_class(self):每個測試類之前執行一次
setup_method(self):在每個測試方法之前執行
test_a方法
teardown_method(self):在每個測試方法之后執行
PASSED
setup_method(self):在每個測試方法之前執行
test_b方法
teardown_method(self):在每個測試方法之后執行
teardown_class(self):每個測試類之后執行一次
"""
(3)函數級
"""
test_03.py
setup_function()和 teardown_function()函數
需要定義在測試類外面,只負責函數的前后置。
對類中定義的方法,不起作用。
"""
import pytest# 函數級,前置函數
def setup_function():print("setup_function: 每個函數開始前都會執行")# 函數級,后置函數
def teardown_function():print("teardown_function: 每個函數結束都會執行\n")# 測試用例a
def test_a():print("類外的test_a函數")assert True# 測試用例b
def test_b():print("類外的test_b函數")assert True# 測試類
class Test_setUp_tearDown:# 測試用例adef test_c(self):print("類內的test_c方法")assert True# 測試用例bdef test_d(self):print("類內的test_d方法")assert Trueif __name__ == '__main__':pytest.main()"""
執行結果:
setup_function: 每個函數開始前都會執行
類外的test_a函數
teardown_function: 每個函數結束都會執行
PASSEDsetup_function: 每個函數開始前都會執行
類外的test_b函數
teardown_function: 每個函數結束都會執行
PASSED類內的test_c方法
PASSED類內的test_d方法
PASSED
"""
(4)模塊級
"""
test_04.py
setup_module()和 teardown_module()函數
需要定義在測試類外面,
對函數和類中定義的方法,都不起作用。
"""
import pytest# 模塊級,前置函數
def setup_module():print("setup_module():在模塊最之前執行\n")# 模塊級,后置函數
def teardown_module():print("teardown_module:在模塊之后執行")# 函數級,前置函數
def setup_function():print("setup_function: 每個函數開始前都會執行")# 函數級,后置函數
def teardown_function():print("teardown_function: 每個函數結束都會執行\n")# 測試用例a
def test_a():print("test_a函數")assert True# 測試用例b
def test_b():print("test_b函數")assert True# 測試類
class Test_setUp_tearDown:# 測試用例adef test_c(self):print("test_c方法")assert True# 測試用例bdef test_d(self):print("test_d方法")assert Trueif __name__ == '__main__':pytest.main() """
setup_module():在模塊最之前執行
setup_function: 每個函數開始前都會執行
test_a函數
teardown_function: 每個函數結束都會執行
PASSEDsetup_function: 每個函數開始前都會執行
test_b函數
teardown_function: 每個函數結束都會執行
PASSEDtest_c方法
PASSEDtest_d方法
teardown_module:在模塊之后執行
PASSED
"""
2. Pytest fixture
定義
1、定義fixture跟定義普通函數差不多,唯一區別就是在函數上加個裝飾器@pytest.fixture(),fixture命名不要用test_開頭,跟用例區分開。用例才是test_開頭的命名;
(裝飾器本質上是一個 Python 函數或類,它可以讓其他函數或類在不需要做任何代碼修改的前提下,在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator),裝飾器的返回值也是一個函數/類對象。)
2、fixture裝飾器里的scope有四個級別的參數:function(不寫默認這個)、class、module、session;
3、fixture可以有返回值,如果沒有return,默認會是None;用例調用fixture的返回值,就是直接把fixture的函數名稱作為參數傳入;
4、fixture可以返回一個元組、列表或字典;
5、測試用例可傳單個、多個fixture參數;
6、fixture與fixture間可相互調用;
2.1 fixture的用法
2.1.1 作為參數使用
2.1.1.1 將fixture函數作為參數傳遞給測試用例
1、返回一個參數
# test_05.py
import pytest@pytest.fixture()
def fix_1():a = 'AAAAAA'print("fix_1 返回一個參數")print(a)return adef test1(fix_1):b = fix_1print("test1 ")print("在test中的值"+b)
'''
fix_1 返回一個參數
AAAAAA
PASSED test1
在test中的值AAAAAA
'''
返回兩個參數
#test_05.py
import pytest
@pytest.fixture()
def fix_2():a = 'var_a'b = "var_b"print("fix_2 返回兩個參數")print("do something before test")yield a,bprint("do something after test")def test2(fix_2):v_a,v_b = fix_2print("test2 ")print("test2 中參數v_a:"+v_a)print("test2 中參數v_b:"+v_b)'''
fix_2 返回兩個參數
do something before test
PASSED test2
test2 中參數v_a:var_a
test2 中參數v_b:var_b
do something after test
'''
2、fixture返回一個元組,list或字典,然后從里面取出對應數據
#test_05.py
import pytest@pytest.fixture()
def fix_3():print('函數fix_3')a = 'leo'b = '123456'return (a, b)def test3(fix_3):print("函數test3")u = fix_3[0]p = fix_3[1]assert u == 'leo'assert p == '123456'print('元祖形式正確')
'''
函數fix_3
PASSED 函數test3
元祖形式正確
'''
2.1.1.2 同一個用例中傳入多個fixture函數
#test_05.py
import pytest@pytest.fixture()
def fix_4():a = 'leo'print('\n fix_4 傳出a')return a@pytest.fixture()
def fix_5():b = '123456'print('fix_5 傳出b')return bdef test4(fix_4, fix_5):print('函數test4')u = fix_4p = fix_5assert u == 'leo'assert p == '123456'print('傳入多個fixture參數正確')'''
test4 fix_4 傳出a
fix_5 傳出b
PASSED 函數test4
傳入多個fixture參數正確
'''
2.1.1.3 fixture函數之間的相互傳遞
#test_06.py
import pytest@pytest.fixture()
def pre_01():print("pre_01 begin")# 前置條件:數據準備之類a ="pre_01 values"yield a# 后置條件,用例結束后的處理,清除數據print("pre_01 end")@pytest.fixture()
def pre_02(pre_01):print("pre_02 begin")# 前置條件:數據準備之類yield# 后置條件,用例結束后的處理,清除數據print("pre_02 end")def test_01(pre_01):print("test_01 01 01")def test_02(pre_02):print("test_02 02 02##")def test_03():print("test_03 03 03 ##")if __name__ == '__main__':pytest.main()'''
pre_01 begin
PASSED test_01 01 01
pre_01 endtest_06.py::test_02
pre_01 begin
pre_02 begin
PASSED test_02 02 02##
pre_02 end
pre_01 endtest_06.py::test_03 PASSED test_03 03 03 ##'''
2.1.2 提供靈活的類似setup和teardown功能
Pytest的fixture另一個強大的功能就是在函數執行前后增加操作,類似setup和teardown操作,但是比setup和teardown的操作更加靈活;
具體使用方式是同樣定義一個函數,然后用裝飾器標記為fixture,然后在此函數中使用一個yield語句,yield語句之前的就會在測試用例之前使用,yield之后的語句就會在測試用例執行完成之后再執行。
#test_07py
@pytest.fixture()
def run_function():print("run before function...")yieldprint("run after function...")def test_run_1(run_function):print("case 1")def test_run_2():print("case 2")def test_run_3(run_function):print("case 3")'''
run before function...
PASSED case 1
run after function...case 2run before function...
PASSED case 3
run after function...'''
常見的應用場景:@pytest.fixture可以用在測試用例執行前后打開、關閉瀏覽器的操作:
#test_07.py
from playwright.sync_api import sync_playwright
@pytest.fixture(scope="session")
def fixture_driver():p = sync_playwright().start()browser = p.chromium.launch(channel="chrome",headless=False)# context = browser.new_context(viewport={'width': 1920, 'height': 1080})context = browser.new_context()context.clear_cookies()yield context# 實現用例后置context.close()browser.close()p.stop()def test_baidu(fixture_driver):page = fixture_driver.new_page()page.goto("http://www.baidu.com")
2.1.3 利用pytest.mark.usefixtures疊加調用多個fixture
如果一個方法或者一個class用例想要同時調用多個fixture,可以使用@pytest.mark.usefixtures()進行疊加。
注意疊加順序,① 與直接傳入fixture不同的是,@pytest.mark.usefixtures無法獲取到被fixture裝飾的函數的返回值;
#test_08.py
import pytest
@pytest.fixture
def func_1():print("用例前置操作---1")yieldprint("用例后置操作---1")@pytest.fixture
def func_2():print("用例前置操作---2")yieldprint("用例后置操作---2")@pytest.fixture
def func_3():print("用例前置操作---3")yieldprint("用例后置操作---3")@pytest.mark.usefixtures("func_3") # 最后執行func_3
@pytest.mark.usefixtures("func_2") # 再執行func_2
@pytest.mark.usefixtures("func_1") # 先執行func_1
def test_func():print("這是測試用例")'''
用例前置操作---1
用例前置操作---2
用例前置操作---3
PASSED 這是測試用例
用例后置操作---3
用例后置操作---2
用例后置操作---1'''
2.1.4 fixture自動使用autouse=True
當用例很多的時候,每次都傳這個參數,會很麻煩。fixture里面有個參數autouse,默認是False沒開啟的,可以設置為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了,autouse設置為True,自動調用fixture功能。所有用例都會生效,包括類中的測試用例和類以外的測試用例。
@pytest.fixture(scope="function", autouse=True)
def fix_func_autouse():print("fix_func_autouse")
#test_09.py
@pytest.fixture(autouse=True, scope="function")
def func_auto():"""autouse為True時,會作用于每一條用例"""print("\n---用例前置操作---")yieldprint("---用例后置操作---")# func_auto函數的autouse=True時,無論是否使用usefixtures引用func_auto,都會執行func_auto
@pytest.mark.usefixtures("func_auto")
def test_01():print("case 1")def test_02():print("case 2")class Test:def test_03(self):print("case 3")'''
test_01
---用例前置操作---
PASSED case 1
---用例后置操作---test_02
---用例前置操作---
PASSED case 2
---用例后置操作---Test::test_03
---用例前置操作---
PASSED case 3
---用例后置操作---
'''
2.2 四種作用域
fixture(scope=‘function’,params=None,autouse=False,ids=None,name=None)
fixture里面有個
function:每一個函數或方法都會調用
class:每一個類調用一次,一個類中可以有多個方法
module:每一個.py文件調用一次,該文件內又有多個function和class
session:多個文件調用一次,可以跨.py文件調用(通常這個級別會結合conftest.py文件使用)
2.2.1 function級別
function默認模式為@pytest.fixture() 函數級別,即scope=“function”,scope可以不寫。每一個函數或方法都會調用,每個測試用例執行前都會執行一次function級別的fixture。
# @pytest.fixture(scope="function")等價于@pytest.fixture()
@pytest.fixture(scope="function")
def func_auto():"""用例級別fixture,作用域單個用例"""print("\n---function級別的用例前置操作---")yieldprint("---function級別的用例后置操作---")# test_01會引用func_auto函數,test_02沒有用修飾器修飾,故不會引用
def test_func_auto_fixture_1(func_auto):print("func 1 print")def test_func_auto_fixture_2():print("func 2 print")
2.2.2 class級別
fixture的scope值還可以是class,此時則fixture定義的動作就會在測試類class的所有用例之前和之后運行,需注意:測試類中只要有一個測試用例的參數中使用了class級別的fixture,則在整個測試類的所有測試用例都會調用fixture函數
① 用例類中的測試用例調用fixture
執行fixture定義的動作,以及此測試類的所有用例結束后同樣要運行fixture指定的動作
import pytest@pytest.fixture(scope="class")
def class_auto():"""類級別fixture,作用域整個類"""print("\n---class級別的用例前置操作---")yieldprint("---class級別的用例后置操作---")class TestClassAutoFixture:# class級別的fixture任意一個用例引用即可def test_class_auto_fixture_1(self, class_auto):print("class 1 print")def test_class_auto_fixture_2(self):print("class 1 print")
測試類中的第1條測試用例引用了fixture修飾的函數,則整個測試類的所有測試用例都會執行fixture函數的前置操作,在所有用例執行完成后,都會執行fixture函數的后置操作。
② 用例類外的測試用例調用fixture
如果在類外的函數中去使用class級別的fixture,則此時在測試類外每個測試用例中,fixture跟function級別的fixture作用是一致的,即
def test_class_auto_fixture(class_auto): print("class 1 print")
測試類外的函數引用了class級別的fixture,則它的作用會等同于function級別的fixture,
2.2.3 module級別
在Python中module即.py文件,當fixture定義為module時,則此fixture將在當前文件中起作用。這里需要特別說明的是,當fixture的scope定義為module時,只要當前文件中有一個測試用例使用了fixture,不管這個用例是在類外,還是在類中,都會在當前文件(模塊)的所有測試用例執行之前去執行fixture定義的行為以及當前文件的所有用例結束之后同樣去執行fixture定義的對應操作。
@pytest.fixture(scope="module")
def module_auto():"""作用于整個py文件"""print("\n---module級別的用例前置操作---")yieldprint("---module級別的用例后置操作---")# 測試類外和測試類內的函數方法都調用了module級別的fixture,但整個py文件只會生效一次fixture。
def test_module_scope_out_class(module_auto):print("case scope 01")class TestScope1:def test_scope_01(self):print("case scope 01")def test_scope_02(self, module_auto):print("case scope 02")def test_scope_03(self):print("case scope 03")
若類中的方法分別調用了class級別的fixture和module級別的fixture,則會兩個fixture都生效:
# 順序在前面fixture會先執行
def test_scope_01(self, module_auto, class_auto): print("case scope 01")
若類中的方法同時調用了function級別、class級別、module級別的fixture,則3種fixture會同時生效:
# 順序在前面fixture會先執行
def test_scope_02(self, module_auto, class_auto, func_auto): print("case scope 02")
2.2.4 session級別(使用conftest.py共享fixture)
當fixture的scope定義為session時,是指在當前目錄下的所有用例之前和之后執行fixture對應的操作
fixture為session級別是可以跨.py模塊調用的,也就是當我們有多個.py文件的用例的時候,如果多個用例只需調用一次fixture,那就可以設置為scope=“session”,并且寫到conftest.py文件里
使用方式:
① 定義測試用例文件
② 在指定目錄下創建conftest.py(固定命名,不可修改)文件,然后在conftest.py文件中定義fixture方法,將scope指定為session,此時在當前目錄下只要有一個用例使用了此fixture,則就會在當前目錄下所有用例之前和之后會執行fixture定義的對應的操作。
@pytest.fixture(scope="session", )
def session_auto():"""session級別的fixture,針對該目錄下的所有用例都生效"""print("\n---session級別的用例前置操作---")yieldprint("---session級別的用例后置操作---")
定義了session級別的fixture,存放于該用例文件的同一個目錄下的conftest.py文件中,該目錄下的任一用例文件中的任一測試用例,引用了這個session級別的fixture,則這個session級別的fixture會針對這整個用例文件會生效。若存放在根目錄下,則針對整個工程的所有用例都會生效。
class TestSessionAutoFixture:# session級別的fixture任意一個用例引用即可def test_session_auto_fixture_1(self, session_auto):print("session 1 print")def test_session_auto_fixture_2(self):print("session 1 print")def test_session_auto_fixture():print("session 1 print")
3. conftest全局作用
Pytest支持在測試的目錄中,創建conftest.py文件,進行全局配置。
conftest.py文件須知:
可以跨.py文件調用,有多個.py文件調用時,可讓conftest.py只調用了一次fixture,或調用多次fixture;
conftest.py與運行的用例要在同一個pakage下,并且有__init__.py文件;
不需要import導入conftest.py,pytest用例會自動識別該文件,放到項目的根目錄下就可以全局目錄調用了,如果放到某個package下,那就在package內有效,可有多個conftest.py;
conftest.py配置腳本名稱是固定的,不能改名稱;
conftest.py文件不能被其他文件導入;
所有同目錄測試文件運行前都會執行conftest.py文件;