pytest.fixture的学习

在使用pytest执行测试脚本时,文件名需要以test_ 开头或者 _test结尾,对于脚本中测试的类以Test_ 开头,函数或方法以test_开头。

先了解下源码里怎么写的注释说明:

def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
  """这是一个标记工厂函数的装饰器.

    不管是否带有参数,这个装饰器是用来定义一个fixture函数.

    fixture函数的函数名被引用后,能够在运行测试用例前被调用:
    测试模块或类可以使用pytest.marker.usefixtures(fixturename)来标记.

    测试用例函数可以直接使用被装饰的fixture名称,作为测试函数的输入参数,在这种情况下,
    fixture函数将返回一个fixture实例来注入测试用例函数,即作为入参传递。

    fixture可以使用‘return’或者‘yield’语句来返回他们的值.使用‘yield’时,
    ‘yield’语句后的代码块将作为teardown代码执行,不管测试结果如何,必须只生成一次,
    也就是说yield语句后的代码只有在最后一次调用后执行.

    :参数 scope: 共享scope范围的fixture, 有以下几种:
                ``"function"`` (默认), ``"class"``, ``"module"``,
                ``"session"``,``"package"``(实验中) .

    :参数 params: 一个可选的参数列表,能够使得fixture函数的多次调用和所有测试用例函数使用它.

    :参数 autouse: 如果为True,那么该fixture函数将被激活,所有测试对象都能使用;
    如果为False,那么激活该fixture函数需要显式引用.

    :参数 ids: 字符串ids的列表,每个id对应着参数,以便于他们是测试id的一部分,
    如果没有提供id,那么将由参数自动生成.

    :参数 name: fixture的名称,默认是被装饰的函数的名字.
    如果一个fixture在同一个模块中被定义使用,那么该fixture的函数名将被请求fixture的函数参数隐藏. 
    解决此问题的方法是命名该装饰函数:
    ``fixture_<fixturename>````@pytest.fixture(name='<fixturename>')``.
    """

fixture的调用

fixture函数和普通函数没有太多的差异,最大的不同就是在需要使用到的函数上面加上装饰器@pytest.fixture(). 函数都是有返回值的,不写return就默认返回None,测试用例在调用fixture时,是将被装饰的函数名作为变量来传参,相当于执行被装饰的函数,接着该函数的返回值也通过函数名传递。

# test_asParameter.py
import pytest

@pytest.fixture()
def test_1():
    varName = 'IPC'
    print('this is varName:%s'%varName)
    return varName

def test_2(test_1):   # 被装饰的函数名test_1作为变量来传参
    assert test_1 == "IPC"   # 该函数test_1的返回值VarName也通过函数名test_1传递

由于tuple、list、dict的存在,函数可以返回其中任意一种形式,所以就这样调用fixture的返回值:

# test_asParameterListTupleDict.py
import pytest

@pytest.fixture()
def test_list():
    varName1 = 'IPC1'
    varName2 = 'IPC2'
    return varName1, varName2

def test_list_(test_list):
    assert isinstance(test_list, tuple)  == True
    assert test_list[0] == "IPC1"
    assert test_list[1] == "IPC2"

以上展示了一个测试用例函数中调用一个fixture函数,一个测试用例函数还可以调用多个不同的fixture:

# test_asParamterMulti.py
import pytest

@pytest.fixture()
def test_func1():
    varName = 'IPC1'
    print("test_func1:",varName)
    return varName

@pytest.fixture()
def test_func2():
    varName = 'IPC2'
    print("test_func2:",varName)
    return varName

@pytest.fixture()
def test_func3():
    varList = ['IPC3', 'IPC4']
    print("test_func3:",varList)
    return varList

def test_funcs(test_func1, test_func2, test_func3):
    assert test_func1 == 'IPC1'
    assert test_func2 == 'IPC2'
    assert test_func3[0] == 'IPC3'
    assert test_func3[1] == 'IPC4'


fixture的作用范围:
@pytest.fixture(scope=‘function’)

scope = “function”为默认值,作用范围是每个测试用例调用前执行一次,销毁是在测试用例调用后,也就是:调用一次销毁一次。

# test_scopefunction.py
import pytest

@pytest.fixture(scope='function')
def test_1():
    varName = "IPC1"
    print("\n execute test_1 ")
    return varName

@pytest.fixture()
def test_2():
    varName = "IPC2"
    print(" execute test_2 ")
    return varName

def test_function1(test_1):
    print(" test_function1 ")
    assert test_1 == "IPC1"

def test_function2(test1,test_2):
    print(" test_function2 ")
    assert test_2 == "IPC2"

运行结果:

 execute test_1 
. test_function1 

 execute test_1 
 execute test_2 
. test_function2 

可以发现"execute test1"打印了两遍,原因就是在scope='function’时,fixture函数调用一次销毁一次。

同理可得,在类中执行测试也是一样的:

import pytest

@pytest.fixture(scope='function')
def test_1():
    varName = "IPC1"
    print("\n execute test_1 ")
    return varName

@pytest.fixture()
def test_2():
    varName = "IPC2"
    print(" execute test_2 ")
    return varName

class Test_function():
    def test_function1(self, test_1):
        print(" test_function1 ")
        assert test_1 == "IPC1"

    def test_function2(self, test_1, test_2):
        print(" test_function2 ")
        assert test_2 == "IPC2"
@pytest.fixture(scope=‘class’)

scope = “class”,若在同一个class中的多个测试用例都调用了同一个scope = “class”的fixture函数,那么在这个class中的所有测试用例调用前,被装饰的fixture函数只执行一次。

import pytest

@pytest.fixture(scope='class')
def test_1():
    varName = "IPC1"
    print("\n execute test_1 once in class")
    return varName

@pytest.fixture(scope='class')
def test_2():
    varName = "IPC2"
    print(" execute test_2 once in class")
    return varName

class Test_class():
    def test_class1(self, test_1, test_2):
        print(" test_class1 ")
        assert test_1 == "IPC1"
        assert test_2 == "IPC2"

    def test_class2(self, test_1, test_2):
        print(" test_class2 ")
        assert test_1 == "IPC1"
        assert test_2 == "IPC2"

def test_3(test_1):
    print(" execute test_3")
    assert test_1 == "IPC1"

运行结果:

 execute test_1 once in class
 execute test_2 once in class
. test_class1 
. test_class2 

 execute test_1 once in class
. execute test_3

可以发现,虽然在Test_class中分别调用了test_1、test_2各两次,但是"execute test_1 once in class"和"execute test_2 once in class"各只打印了一遍;“execute test_1 once in class"第二次打印是因为调用它的不是该类中的函数,而是普通的函数。所以当scope='class’时,对于该Test_class中的所有测试用例,在调用fixture前,fixture函数只执行一次。这样可以减少fixture函数的反复执行,提高运行效率。

@pytest.fixture(scope=‘module’)

scope=‘module’,在当前运行的脚本中(即当前module,在Python中一个.py脚本视为一个module),所有的测试用例在调用fixture前,被装饰的fixture函数只执行一次。

import pytest

@pytest.fixture(scope='module')
def test_1():
    varName = "IPC1"
    print("\n only execute test_1 one time ")
    return varName

@pytest.fixture(scope='module')
def test_2():
    varName = "IPC2"
    print("only execute test_2 one time ")
    return varName

def test_module1(test_1):
    print("func test_module1 ")
    assert test_1 == "IPC1"

def test_module2(test_1, test_2):
    print("func test_module2 ")
    assert test_2 == "IPC2"

class Test_module():
    def test_module1(self, test_1, test_2):
        print(" class test_module1 ")
        assert test_1 == "IPC1"
        assert test_2 == "IPC2"

    def test_module2(self, test_1, test_2):
        print(" class test_module2 ")
        assert test_1 == "IPC1"
        assert test_2 == "IPC2"

运行结果:

 only execute test_1 one time 
.func test_module1 
only execute test_2 one time 
.func test_module2 
. class test_module1 
. class test_module2 

可以发现,虽然分别调用了test_1、test_2多次,但是"only execute test_1 one time"和"only execute test_2 one time"只打印了一遍,原因就是当scope='module’时,对于该Test_class中的所有测试用例(无论是类还是类外的函数),在调用fixture前,fixture函数只执行一次。

@pytest.fixture(scope=‘session’)

scope=‘session’,在一个工程项目中有多个py脚本,每个脚本中有一个或多个测试用例,当使用scope='session’来装饰fixture函数时,多个测试用例调用fixture函数前,该fixture函数只用执行一次。为了统一管理这些fixture函数,pytest中使用conftest.py文件来放置参数为scope='session’的fixture函数,conftest.py的名字是固定的,pytest在运行时会自动识别。

# conftest.py
import pytest

@pytest.fixture(scope='session')
def test_session1():
    varName = "IPC1"
    print("\n  test_session1 in conftest.py ")
    return varName

@pytest.fixture(scope='session')
def test_session2():
    varName = "IPC2"
    print("test_session2 conftest.py ")
    return varName
# test_session.py
def test_1(test_session1):
    print("func test_1 in test_session.py ")
    assert test_session1 == "IPC1"

def test_2(test_session1, test_session2):
    print("func test_2 in test_session.py ")
    assert test_session2 == "IPC2"

class Test_module():
    def test_module1(self, test_session1, test_session2):
        print(" class test_sesson1 ")
        assert test_session1 == "IPC1"
        assert test_session2 == "IPC2"

    def test_module2(self, test_session1, test_session2):
        print(" class test_sesson2 ")
        assert test_session1 == "IPC1"
        assert test_session2 == "IPC2"

运行结果:

test_session.py 
  test_session1 in conftest.py 
.func test_1 in test_session.py 
test_session2 conftest.py 
.func test_2 in test_session.py 
. class test_sesson1 
. class test_sesson2 

可以看出,在test_session.py文件中可以调用conftest.py中装饰器参数为scoope=‘session’的fixture函数,而且和’class’、'module’相似,带有scope='session’的fixture函数在被所有测试用例函数调用前,只执行一次。


@pytst.mark.usefixtures()的作用

@pytst.mark.usefixtures()标记需要执行的测试用例, 但是使用它无法获取到被调用函数的返回值;而不使用标记,同时被调用的函数被类似于@pytest.fixtures()装饰时,测试用例函数可以通过调用fixture函数,获取返回值。另外,这个装饰器是写在要执行的测试用例函数上面的,而类似于@pytest.fixture(scope=‘function’)是写在被调用的fixture函数上面,以供测试用例函数调用。

当测试用例不需要返回值时,可以考虑使用@pytst.mark.usefixtures(),两者都行;测试用例需要返回值时只能使用@pytest.fixture()来调用。

import pytest

@pytest.fixture()
def test_1():
    print("\n this is test_1")

@pytest.mark.usefixtures('test_1')
def test_markusefixtures():
    print("this is test_markusefixtures")

@pytest.mark.usefixtures('test_1')
class Test_markusefixtures():
    def test_1(self):
        print("Test_markusefixtures1")
    def test_2(self):
        print("Test_markusefixtures2")

运行结果:

 this is test_1
.this is test_markusefixtures

 this is test_1
.Test_markusefixtures1

 this is test_1
.Test_markusefixtures2
多个@pytst.mark.usefixtures()作用于同一个测试用例

当需要多个@pytst.mark.usefixtures()装饰一个测试用例时,可以直接在测试用例代码上面添加,不过放置底层的装饰器先执行,最上层的装饰器最后执行。

import pytest

@pytest.fixture()
def test_1():
    print("\n this is test_1")

@pytest.fixture()
def test_2():
    print("\n this is test_2")

@pytest.mark.usefixtures('test_1')
@pytest.mark.usefixtures('test_2')
class Test_markusefixtures():
    def test_mark1(self):
        print("Test_markusefixtures1")
    def test_mark2(self):
        print("Test_markusefixtures2")

运行结果

  this is test_2

 this is test_1
.Test_markusefixtures1

 this is test_2

 this is test_1
.Test_markusefixtures2

autouse 参数的作用

在fixture的参数中,autouse默认值为False。当autouse为True时,测试用例会自动调用被@pytest.fixture(autouse=‘True’)装饰的函数。当一个项目或者脚本中存在很多基础的函数需要被调用,那么使用autouse=True会比较方便,省去了测试用装饰器中的传参。

import pytest

@pytest.fixture(scope='function', autouse=True)
def test_function_scope():
    print("\n I'm function_scope, I need to be called forever.")

@pytest.fixture(scope='class', autouse=True)
def test_class_scope():
    print("\n I'm class_scope, I need to be called forever.")

@pytest.fixture(scope='module', autouse=True)
def test_module_scope():
    print("\n I'm module_scope, I need to be called forever.")

def test_1():
    print("test_1: I will call ... ")

class Test_autouse():
    def test_2(self):
        print("test_2: I will call ... ")

    def test_3(self):
        print("test_3: I will call ... ")

运行结果:

 I'm module_scope, I need to be called forever.

 I'm class_scope, I need to be called forever.

 I'm function_scope, I need to be called forever.
.test_1: I will call ... 

 I'm class_scope, I need to be called forever.

 I'm function_scope, I need to be called forever.
.test_2: I will call ... 

 I'm function_scope, I need to be called forever.
.test_3: I will call ... 
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值