pytest測試框架系列 - Pytest Fixture和conftest.py還能這樣使用蕾总?

前言

Fixture是pytest的非常核心功能之一戴尸,在不改變被裝飾函數(shù)的前提下對函數(shù)進(jìn)行功能增強(qiáng)粟焊,經(jīng)常用于測試用例前置和后置工作。與setup/teardown類似孙蒙,但更強(qiáng)大靈活项棠。

fixture的優(yōu)勢

  • fixture命名方式靈活,不局限于 setup 和teardown 那幾個(gè)命名規(guī)則
  • conftest.py 配置里可以實(shí)現(xiàn)數(shù)據(jù)共享挎峦,能夠自動(dòng)搜索需要的fixture
  • fixture 配置不同的參數(shù)可以輕松實(shí)現(xiàn)跨文件香追、session會話共享

fixture工作原理

  • 在普通函數(shù)上使用@Pytest.fixture()裝飾器,聲明函數(shù)為一個(gè)fixture函數(shù)
  • 如果測試用例函數(shù)的參數(shù)列表中存在fixture的函數(shù)名或者使用@pytest.mark.usefixtures(fixture_name)
  • 在測試執(zhí)行階段坦胶,pytest執(zhí)行用例之前執(zhí)行fixture函數(shù)功能
  • 如果fixture裝飾的函數(shù)無返回值透典,相當(dāng)于用例前置操作,否則相當(dāng)于傳參
  • 如果fixture設(shè)置了后置操作顿苇,則用例執(zhí)行完成后進(jìn)行執(zhí)行

fixture搜索順序

  • 第一步:優(yōu)先搜索測試所在的模塊
  • 第二步:搜索模塊同一文件路徑下的conftest.py
  • 第三步:找不到再搜索上一層的conftest.py峭咒,直到項(xiàng)目根目錄

fixture 函數(shù)說明

def fixture(
    fixture_function: Optional[_FixtureFunction] = None,
    *,
    scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
    params: Optional[Iterable[object]] = None,
    autouse: bool = False,
    ids: Optional[
        Union[
            Iterable[Union[None, str, float, int, bool]],
            Callable[[Any], Optional[object]],
        ]
    ] = None,
    name: Optional[str] = None,
) -> Union[FixtureFunctionMarker, _FixtureFunction]:
    """Decorator to mark a fixture factory function.

    This decorator can be used, with or without parameters, to define a
    fixture function.

    The name of the fixture function can later be referenced to cause its
    invocation ahead of running tests: test modules or classes can use the
    ``pytest.mark.usefixtures(fixturename)`` marker.

    Test functions can directly use fixture names as input arguments in which
    case the fixture instance returned from the fixture function will be
    injected.

    Fixtures can provide their values to test functions using ``return`` or
    ``yield`` statements. When using ``yield`` the code block after the
    ``yield`` statement is executed as teardown code regardless of the test
    outcome, and must yield exactly once.

    :param scope:
        The scope for which this fixture is shared; one of ``"function"``
        (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``.

        This parameter may also be a callable which receives ``(fixture_name, config)``
        as parameters, and must return a ``str`` with one of the values mentioned above.

        See :ref:`dynamic scope` in the docs for more information.

    :param params:
        An optional list of parameters which will cause multiple invocations
        of the fixture function and all of the tests using it. The current
        parameter is available in ``request.param``.

    :param autouse:
        If True, the fixture func is activated for all tests that can see it.
        If False (the default), an explicit reference is needed to activate
        the fixture.

    :param ids:
        List of string ids each corresponding to the params so that they are
        part of the test id. If no ids are provided they will be generated
        automatically from the params.

    :param name:
        The name of the fixture. This defaults to the name of the decorated
        function. If a fixture is used in the same module in which it is
        defined, the function name of the fixture will be shadowed by the
        function arg that requests the fixture; one way to resolve this is to
        name the decorated function ``fixture_<fixturename>`` and then use
        ``@pytest.fixture(name='<fixturename>')``.
    """
    """
    翻譯:
     可以使用此裝飾器(帶或不帶參數(shù))來定義fixture功能。 
     
     fixture功能的名稱可以在后面使用引用它會在運(yùn)行測試之前調(diào)用它:test模塊或類可以使用
     pytest.mark.usefixtures(fixturename標(biāo)記)纪岁。 
     
     測試功能可以直接使用fixture名稱作為輸入?yún)?shù)凑队,在這種情況下,夾具實(shí)例從fixture返回功能將被注入幔翰。
     
     fixture可以使用``return`` or``yield`` 語句返回測試函數(shù)所需要的參數(shù)值漩氨,在``yield``后面的代碼塊
     不管測試的執(zhí)行結(jié)果怎么樣都會執(zhí)行
     
    :arg scope: scope 有四個(gè)級別參數(shù) "function" (默認(rèn)), "class", "module" or "session".

    :arg params: 一個(gè)可選的參數(shù)列表,它將導(dǎo)致多個(gè)參數(shù)調(diào)用fixture功能和所有測試使用它

    :arg autouse:  如果為True遗增,自動(dòng)執(zhí)行fixture函數(shù)才菠,所有函數(shù)都可以使用。 如果為False(默認(rèn)值)
    則手動(dòng)使用fixture才能調(diào)用

    :arg ids: 每個(gè)字符串id的列表贡定,每個(gè)字符串對應(yīng)于params 這樣他們就是測試ID的一部分赋访。 
    如果沒有提供ID它們將從params自動(dòng)生成

    :arg name:   fixture的名稱。 默認(rèn)為裝飾函數(shù)的名稱缓待。 如果設(shè)置了name的值蚓耽,在使用fixture時(shí)需要
    使用設(shè)置的name,否則無法識別
    """

fixture參數(shù)詳解

使用語法:

@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None)
def login():
    print("我是login函數(shù)")

scope 參數(shù)

  • 說明:可以認(rèn)為是fixture的作用域旋炒,默認(rèn):function步悠,還有class、module瘫镇、package鼎兽、session四個(gè)
  • 區(qū)別:
取值 范圍 說明
function 函數(shù)級 每一個(gè)函數(shù)或方法都會調(diào)用
class 類級 每個(gè)測試類只運(yùn)行一次
module 模塊級 每一個(gè).py文件調(diào)用一次
session 會話級 每次會話只需要運(yùn)行一次答姥,一般用于打開瀏覽器、啟動(dòng)APP谚咬、登錄等操作

關(guān)注點(diǎn)

  • fixture可以給函數(shù)和類使用
  • fixture通過測試用例參數(shù)方式使用鹦付,這樣fixture的返回值可以通過fixture名稱作為參數(shù)傳遞給測試用例,也可以使用@pytest.mark.usefixtures("login")方式择卦,但無法給測試用例傳遞fixture的返回值
  • fixture函數(shù)內(nèi)可以調(diào)用其他fixture函數(shù)敲长,必須要通過函數(shù)參數(shù)方式
  • 非測試用例的函數(shù)無法使用fixture
    作用范圍示例:

scope = "function"

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 14:39
# @Author  : king
# @File    :test_fixture.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(scope="function")
def login():
    print("我是login函數(shù)。秉继。")
    return "king"

# fixture內(nèi)調(diào)用其他fixture
@pytest.fixture(scope="function")
def login_01(login):
    print("我是調(diào)用了 login的fixture函數(shù)")

# 通過參數(shù)方式使用fixture
def test_01(login):
    print("我是 test_01 測試用例\n")

# 通過@pytest.mark.usefixtures("login")調(diào)用fixture
@pytest.mark.usefixtures("login")
def test_02():
    print("我是 test_02 測試用例\n")

# 通過參數(shù)方式使用調(diào)用其他fixture的fixture
def test_03(login_01):
    print("我是 test_03 測試用例\n")

# 給類使用fixture
@pytest.mark.usefixtures("login")
class TestFixture:
    def test_one(self):
        print("我是類里面的 test_one 測試用例")

    def test_two(self):
        print("我是類里面的 test_two 測試用例")

# 非test開頭函數(shù)調(diào)用fixture
def reg_01(login):
    print("我是reg函數(shù)\n")

# 非test開頭函數(shù)使用@pytest.mark.usefixtures("login")調(diào)用fixture
@pytest.mark.usefixtures("login")
def reg_02():
    print("我是reg函數(shù)\n")

if __name__ == '__main__':
    pytest.main(["-s", "test_fixture.py"])


執(zhí)行結(jié)果為:


在這里插入圖片描述

scope = "class"

示例:

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 15:35
# @Author  : king
# @File    :test_fixture_class.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(scope="class")
def login():
    print("我是login函數(shù)祈噪。。")
    return "king"

# 通過參數(shù)方式使用fixture
def test_01(login):
    print("我是 test_01 測試用例\n")

# 通過參數(shù)方式使用fixture
def test_02(login):
    print("我是 test_02 測試用例\n")

@pytest.mark.usefixtures("login")
class TestFixture:
    def test_one(self):
        print("我是類里面的 test_one 測試用例")

    def test_two(self):
        print("我是類里面的 test_two 測試用例")

if __name__ == '__main__':
    pytest.main(["-s", "test_fixture_class.py"])

執(zhí)行結(jié)果:


在這里插入圖片描述

scope = "module"

示例:

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 16:52
# @Author  : king
# @File    :test_fixture_module.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(scope="module")
def login():
    print("我是login函數(shù)尚辑。辑鲤。")
    return "king"

# 通過參數(shù)方式使用fixture
def test_01(login):
    print("我是 test_01 測試用例\n")

# 通過參數(shù)方式使用fixture
def test_02(login):
    print("我是 test_02 測試用例\n")

@pytest.mark.usefixtures("login")
class TestFixture:
    def test_one(self):
        print("我是類里面的 test_one 測試用例")

    def test_two(self):
        print("我是類里面的 test_two 測試用例")

if __name__ == '__main__':
    pytest.main(["-s", "test_fixture_module.py"])

執(zhí)行結(jié)果為:


在這里插入圖片描述

scope = "session"

  • 注意:session代表會話級,就是從啟動(dòng)測試到結(jié)束測試杠茬,看作為一次session會話遂填,scope = "session"使用到后面conftest.py詳細(xì)講解

params 參數(shù)

  • fixture的可選形參列表,支持列表傳入
  • params 參數(shù)包含幾個(gè)澈蝙,調(diào)用時(shí)就會執(zhí)行幾次
  • 可與參數(shù)ids一起使用吓坚,作為每個(gè)參數(shù)的標(biāo)識,詳見ids
  • 需要使用時(shí)灯荧,參數(shù)調(diào)用寫法固定為:Request.param
    示例:
# _*_coding:utf-8 _*_
# @Time  :2021/7/3 17:13
# @Author  : king
# @File    :test_fixture_params.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(params=[1, 2, 3, 4, 5])
def login(request):
    print("我是login函數(shù)礁击。。")
    return request.param

# 通過參數(shù)方式使用fixture
def test_01(login):
    print("我是 test_01 測試用例-params- {}\n".format(login))

if __name__ == '__main__':
    pytest.main(["-s", "test_fixture_params.py"])

執(zhí)行結(jié)果:


在這里插入圖片描述

autouse 參數(shù)

  • 默認(rèn)False
  • 如果設(shè)置為True逗载,則每個(gè)測試函數(shù)都會自動(dòng)調(diào)用該fixture,無需傳入fixture函數(shù)名哆窿,作用范圍跟著scope走(注意使用)
    示例:
# _*_coding:utf-8 _*_
# @Time  :2021/7/3 17:25
# @Author  : king
# @File    :test_fixture_autouse.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(autouse=True)
def login():
    print("我是login函數(shù)。厉斟。")

# 通過參數(shù)方式使用fixture
def test_01():
    print("我是 test_01 測試用例\n")

if __name__ == '__main__':
    pytest.main(["-s", "test_fixture_autouse.py"])

執(zhí)行結(jié)果:


在這里插入圖片描述

ids 參數(shù)

  • 用例標(biāo)題挚躯,需要與params配合使用,一對一關(guān)系

未配置ids時(shí):

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 17:29
# @Author  : king
# @File    :test_fixture_ids.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(params=[1, 2, 3])
def login(request):
    return request.param

# 通過參數(shù)方式使用fixture
def test_01(login):
    print("我是 test_01 測試用例\n")

if __name__ == '__main__':
    pytest.main(["-s", "test_fixture_ids.py"])

執(zhí)行結(jié)果:
[圖片上傳失敗...(image-7d1ae1-1625479261110)]

配置了ids的示例:

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 17:29
# @Author  : king
# @File    :test_fixture_ids.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(params=[1, 2, 3], ids=["case01", "case02", "case03"])
def login(request):
    return request.param

# 通過參數(shù)方式使用fixture
def test_01(login):
    print("我是 test_01 測試用例\n")

if __name__ == '__main__':
    pytest.main(["-s", "test_fixture_ids.py"])

執(zhí)行結(jié)果:


在這里插入圖片描述

問題:

當(dāng)我們多個(gè)測試用例文件(test_*.py)的所有用例都需要用登錄擦秽、打開瀏覽器码荔、啟動(dòng)APP等功能來作為前置操作,那就不能把登錄感挥、打開瀏覽器缩搅、啟動(dòng)APP功能寫到某個(gè)用例文件,如何解決呢触幼?

解決方案:引入conftest.py文件硼瓣,為了解決上述問題,單獨(dú)管理一些全局的fixture

conftest.py使用

說明:

  • pytest會默認(rèn)讀取conftest.py里面的所有fixture
  • conftest.py 文件名稱是固定的置谦,不能隨意改動(dòng)
  • conftest.py只對同一個(gè)package下的所有測試用例生效堂鲤,并且有init.py文件
  • 不同目錄可以有自己的conftest.py亿傅,一個(gè)項(xiàng)目中可以有多個(gè)conftest.py
  • pytest會自動(dòng)查找項(xiàng)目中的conftest.py文件,逐層往上查找

示例:

  • 項(xiàng)目目錄
case
│  conftest.py
│  __init__.py
│
├─case01
│      conftest.py
│      test_01.py
│      __init__.py
│
└─case02
        conftest.py
        test_02.py
        __init__.py

case/conftest.py

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 18:04
# @Author  : king
# @File    :conftest.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(scope="session")
def login():
    print("我是case目錄的login")

@pytest.fixture(scope="session")
def open_browser():
    print("我是case目錄的 open_browser")

case01/conftest.py

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 18:06
# @Author  : king
# @File    :conftest.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(scope="session")
def login():
    print("我是case01目錄的login")

case01/conftest.py

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 18:04
# @Author  : king
# @File    :test_01.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】

def test_01(login):
    print("我是case01 里面test_01 測試用例")

def test_02(open_browser):
    print("我是case01 里面test_02 測試用例")

case02/conftest.py

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 18:07
# @Author  : king
# @File    :conftest.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest

@pytest.fixture(scope="session")
def _login():
    print("我是case02 目錄的login")

case02/test_02.py

# _*_coding:utf-8 _*_
# @Time  :2021/7/3 18:04
# @Author  : king
# @File    :test_02.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】

def test_02(login):
    print("我是case02的 test_02 測試用例")

在case目錄下瘟栖,執(zhí)行 pytest -s

在這里插入圖片描述

注意點(diǎn)

  • 外層的fixture不能調(diào)用內(nèi)層的fixture
  • 不同包下面的fixture只能當(dāng)前包使用葵擎,不能被其他包使用,例如case01下面的fixture不能被case02的測試函數(shù)使用

以上為內(nèi)容純屬個(gè)人理解慢宗,如有不足坪蚁,歡迎各位大神指正奔穿,轉(zhuǎn)載請注明出處!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搜变,更是在濱河造成了極大的恐慌凤巨,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件男摧,死亡現(xiàn)場離奇詭異蔬墩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)耗拓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門拇颅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乔询,你說我怎么就攤上這事樟插。” “怎么了竿刁?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵黄锤,是天一觀的道長。 經(jīng)常有香客問我食拜,道長鸵熟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任负甸,我火速辦了婚禮流强,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呻待。我一直安慰自己煮盼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布带污。 她就那樣靜靜地躺著僵控,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鱼冀。 梳的紋絲不亂的頭發(fā)上报破,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天悠就,我揣著相機(jī)與錄音,去河邊找鬼充易。 笑死梗脾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盹靴。 我是一名探鬼主播炸茧,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼稿静!你這毒婦竟也來了梭冠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤改备,失蹤者是張志新(化名)和其女友劉穎控漠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悬钳,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盐捷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了默勾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碉渡。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖母剥,靈堂內(nèi)的尸體忽然破棺而出滞诺,到底是詐尸還是另有隱情,我是刑警寧澤媳搪,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布铭段,位于F島的核電站,受9級特大地震影響秦爆,放射性物質(zhì)發(fā)生泄漏序愚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一等限、第九天 我趴在偏房一處隱蔽的房頂上張望爸吮。 院中可真熱鬧,春花似錦望门、人聲如沸形娇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桐早。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哄酝,已是汗流浹背友存。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陶衅,地道東北人屡立。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像搀军,于是被迫代替她去往敵國和親膨俐。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容