1. 程式人生 > 實用技巧 >pytest框架fixture的使用

pytest框架fixture的使用

fixture可以當做引數傳入

定義fixture跟定義普通函式差不多,唯一區別就是在函式上加個裝飾器@pytest.fixture(),fixture命名不要以test開頭,跟用例區分開。fixture是有返回值得,沒有返回值預設為None。用例呼叫fixture的返回值,直接就是把fixture的函式名稱當做變數名稱。

ex:

import pytest

@pytest.fixture()
def test1():
    a = 'leo'
    return a


def test2(test1):
    assert test1 == 'leo'


if __name__ == '__main__':
    pytest.main('-q test_fixture.py')


輸出:
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 1 item

test_fixture.py .                                                        [100%]

========================== 1 passed in 0.02 seconds ===========================
Process finished with exit code 0

使用多個fixture

如果用例需要用到多個fixture的返回資料,fixture也可以返回一個元祖,list或字典,然後從裡面取出對應資料。

ex:

import pytest

@pytest.fixture()
def test1():
    a = 'leo'
    b = '123456'
    print('傳出a,b')
    return (a, b)


def test2(test1):
    u = test1[0]
    p = test1[1]
    assert u == 'leo'
    assert p == '123456'
    print('元祖形式正確')


if __name__ == '__main__':
    pytest.main('-q test_fixture.py')


輸出結果:
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 1 item

test_fixture.py 傳出a,b
.元祖形式正確
                                                        [100%]

========================== 1 passed in 0.02 seconds ===========================
Process finished with exit code 0

當然也可以分成多個fixture,然後在用例中傳多個fixture引數

import pytest


@pytest.fixture()
def test1():
    a = 'leo'
    print('\n傳出a')
    return a


@pytest.fixture()
def test2():
    b = '123456'
    print('傳出b')
    return b


def test3(test1, test2):
    u = test1
    p = test2
    assert u == 'leo'
    assert p == '123456'
    print('傳入多個fixture引數正確')


if __name__ == '__main__':
    pytest.main('-q test_fixture.py')



輸出結果:
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 1 item

test_fixture.py 
傳出a
傳出b
.傳入多個fixture引數正確

fixture互相呼叫

import pytest


@pytest.fixture()
def test1():
    a = 'leo'
    print('\n傳出a')
    return a


def test2(test1):
    assert test1 == 'leo'
    print('fixture傳參成功')


if __name__ == '__main__':
    pytest.main('-q test_fixture.py')


輸出結果:
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 1 item

test_fixture.py 
傳出a
.fixture傳參成功
                                                        [100%]

========================== 1 passed in 0.03 seconds ===========================
Process finished with exit code 0

介紹完了fixture的使用方式,現在介紹一下fixture的作用範圍(scope)

fixture的作用範圍

fixture裡面有個scope引數可以控制fixture的作用範圍:session>module>class>function

-function:每一個函式或方法都會呼叫

-class:每一個類呼叫一次,一個類中可以有多個方法

-module:每一個.py檔案呼叫一次,該檔案內又有多個function和class

-session:是多個檔案呼叫一次,可以跨.py檔案呼叫,每個.py檔案就是module

fixture原始碼詳解

fixture(scope='function',params=None,autouse=False,ids=None,name=None):

scope:有四個級別引數"function"(預設),"class","module","session"

params:一個可選的引數列表,它將導致多個引數呼叫fixture功能和所有測試使用它。

autouse:如果True,則為所有測試啟用fixture func可以看到它。如果為False則顯示需要參考來啟用fixture

ids:每個字串id的列表,每個字串對應於params這樣他們就是測試ID的一部分。如果沒有提供ID它們將從params自動生成

name:fixture的名稱。這預設為裝飾函式的名稱。如果fixture在定義它的統一模組中使用,夾具的功能名稱將被請求夾具的功能arg遮蔽,解決這個問題的一種方法時將裝飾函式命令"fixture_<fixturename>"然後使用"@pytest.fixture(name='<fixturename>')"。

具體闡述一下scope四個引數的範圍

scope="function"

@pytest.fixture()如果不寫引數,引數就是scope="function",它的作用範圍是每個測試用例來之前執行一次,銷燬程式碼在測試用例之後執行。

import pytest


@pytest.fixture()
def test1():
    a = 'leo'
    print('\n傳出a')
    return a


@pytest.fixture(scope='function')
def test2():
    b = '男'
    print('\n傳出b')
    return b


def test3(test1):
    name = 'leo'
    print('找到name')
    assert test1 == name


def test4(test2):
    sex = '男'
    print('找到sex')
    assert test2 == sex


if __name__ == '__main__':
    pytest.main('-q test_fixture.py')


輸出結果:

platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 2 items

test_fixture.py 
傳出a
.找到name

傳出b
.找到sex
                                                       [100%]

========================== 2 passed in 0.04 seconds ===========================

放在類中實現結果也是一樣的

import pytest


@pytest.fixture()
def test1():
    a = 'leo'
    print('\n傳出a')
    return a


@pytest.fixture(scope='function')
def test2():
    b = '男'
    print('\n傳出b')
    return b


class TestCase:
    def test3(self, test1):
        name = 'leo'
        print('找到name')
        assert test1 == name

    def test4(self, test2):
        sex = '男'
        print('找到sex')
        assert test2 == sex


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

輸出結果:

platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 2 items

test_fixture.py 
傳出a
.找到name

傳出b
.找到sex
                                                       [100%]

========================== 2 passed in 0.03 seconds ===========================
Process finished with exit code 0

scope="class"

fixture為class級別的時候,如果一個class裡面有多個用例,都呼叫了次fixture,那麼此fixture只在此class裡所有用例開始前執行一次。

import pytest


@pytest.fixture(scope='class')
def test1():
    b = '男'
    print('傳出了%s, 且只在class裡所有用例開始前執行一次!!!' % b)
    return b


class TestCase:
    def test3(self, test1):
        name = '男'
        print('找到name')
        assert test1 == name

    def test4(self, test1):
        sex = '男'
        print('找到sex')
        assert test1 == sex


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

輸出結果:
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 2 items

test_fixture.py 傳出了男, 且只在class裡所有用例開始前執行一次!!!
.找到name
.找到sex
                                                       [100%]

========================== 2 passed in 0.05 seconds ===========================
Process finished with exit code 0

scope="module"

fixture為module時,在當前.py腳本里面所有用例開始前只執行一次。

import pytest
##test_fixture.py

@pytest.fixture(scope='module')
def test1():
    b = '男'
    print('傳出了%s, 且在當前py檔案下執行一次!!!' % b)
    return b


def test3(test1):
    name = '男'
    print('找到name')
    assert test1 == name


class TestCase:

    def test4(self, test1):
        sex = '男'
        print('找到sex')
        assert test1 == sex


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


輸出結果:
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 2 items

test_fixture.py 傳出了男, 且在當前py檔案下執行一次!!!
.找到sex
.找到name
                                                       [100%]

========================== 2 passed in 0.03 seconds ===========================
Process finished with exit code 0

scope="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():
    sex = '男'
    print('獲取到%s' % sex)
    return sex
import pytest
# test_fixture.py

def test3(test1):
    name = '男'
    print('找到name')
    assert test1 == name


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

class TestCase:

    def test4(self, test1):
        sex = '男'
        print('找到sex')
        assert test1 == sex


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

如果需要同時執行兩個py檔案,可以在cmd中在檔案py檔案所在目錄下執行命令:pytest -s test_fixture.py test_fixture1.py

執行結果為:

================================================= test session starts =================================================
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:
collected 2 items

test_fixture.py 獲取到男
找到name
.
test_fixture1.py 找到sex
.

============================================== 2 passed in 0.05 seconds ===============================================

呼叫fixture的三種方法

1.函式或類裡面方法直接傳fixture的函式引數名稱

import pytest
# test_fixture1.py


@pytest.fixture()
def test1():
    print('\n開始執行function')


def test_a(test1):
    print('---用例a執行---')


class TestCase:

    def test_b(self, test1):
        print('---用例b執行')

輸出結果:
test_fixture1.py 
開始執行function
.---用例a執行---

開始執行function
.---用例b執行
                                                      [100%]

========================== 2 passed in 0.05 seconds ===========================
Process finished with exit code 0

2.使用裝飾器@pytest.mark.usefixtures()修飾需要執行的用例

import pytest
# test_fixture1.py


@pytest.fixture()
def test1():
    print('\n開始執行function')


@pytest.mark.usefixtures('test1')
def test_a():
    print('---用例a執行---')


@pytest.mark.usefixtures('test1')
class TestCase:

    def test_b(self):
        print('---用例b執行---')

    def test_c(self):
        print('---用例c執行---')


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

輸出結果:
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 3 items

test_fixture1.py 
開始執行function
.---用例a執行---

開始執行function
.---用例b執行---

開始執行function
.---用例c執行---
                                                     [100%]

========================== 3 passed in 0.06 seconds ===========================
Process finished with exit code 0

疊加usefixtures

如果一個方法或者一個class用例想要同時呼叫多個fixture,可以使用@pytest.mark.usefixture()進行疊加。注意疊加順序,先執行的放底層,後執行的放上層。

import pytest
# test_fixture1.py


@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 TestCase:

    def test_b(self):
        print('---用例b執行---')

    def test_c(self):
        print('---用例c執行---')


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


輸出結果:
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 3 items

test_fixture1.py 
開始執行function2

開始執行function1
.---用例a執行---

開始執行function1

開始執行function2
.---用例b執行---

開始執行function1

開始執行function2
.---用例c執行---
                                                     [100%]

========================== 3 passed in 0.03 seconds ===========================
Process finished with exit code 0

usefixtures與傳fixture區別

如果fixture有返回值,那麼usefixture就無法獲取到返回值,這個是裝飾器usefixture與用例直接傳fixture引數的區別。

當fixture需要用到return出來的引數時,只能講引數名稱直接當引數傳入,不需要用到return出來的引數時,兩種方式都可以。

fixture自動使用autouse=True

當用例很多的時候,每次都傳這個引數,會很麻煩。fixture裡面有個引數autouse,預設是False沒開啟的,可以設定為True開啟自動使用fixture功能,這樣用例就不用每次都去傳參了

autouse設定為True,自動呼叫fixture功能

import pytest
# test_fixture1.py


@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 TestCase:

    def test_b(self):
        print('---用例b執行---')

    def test_c(self):
        print('---用例c執行---')


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


輸出結果:
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-4.0.2, py-1.7.0, pluggy-0.8.0
rootdir: C:\Program Files\PycharmProjects\exercise, inifile:collected 4 items

test_fixture1.py 
開始執行module

開始執行class

開始執行function
.---用例a執行---

開始執行class

開始執行function
.---用例d執行---

開始執行class

開始執行function
.---用例b執行---

開始執行function
.---用例c執行---
                                                    [100%]