pytest之fixture的使用

pytest测试用例编写规则:

文件名以test_.py文件和_test.py
以test_开头的函数
以Test开头的类
以test_开头的方法

@pytest.fixture()的使用:

注:执行命令时使用-s参数可以将执行用例中的print打印函数,如没有使用,pytest用例中的print函数将不会被执行

pytest -s test_fixture.py
pytest --fixtures  xx.py   可以查看某文件里的fixture

一,fixture当参数传入

  1. fixture可以当做参数传入,定义fixture跟定义普通函数差不多,唯一区别就是在函数上加个装饰器@pytest.fixture()
  2. fixture命名不要以test开头,跟用例区分开
  3. fixture是有返回值的,没有返回值默认为None
  4. 用例调用fixture的返回值,直接就是把fixture的函数名称当做变量名称。
@pytest.fixture()
def creat_id():
    a=2
    return a*3
    
def test_compute(creat_id):
    some=creat_id+4
    assert some==11

creat_id = 6

    def test_compute(creat_id):
        some=creat_id+4
>       assert some==11
E       assert 10 == 11
test_study.py:42: AssertionError

在这里故意用例执行失败,通过回溯可以看到
1,test_compute测试函数需要一个名为的函数参数creat_id,通过查找名为的带有fixture标记的函数,可以找到匹配的夹具函数creat_id
2,creat_id() 被称为创建实例,得到creat_id=6
3,test_compute函数开始执行,断言失败

当fixture标记的函数没有返回值时,执行用例,改fixture标记函数返回值默认为None

creat_id = None

    def test_compute(creat_id):
>       some=creat_id+4
E       TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
test_study.py:41: TypeError

如果想要实现该creat_id函数能够被其他模块/类等共享使用,可以把写入到conftest.py文件,并写上scope对应的值,对于可能的值scope有:function,class,module,package或session。不填写默认function

二,多个fixture使用

1.如果用例需要用到多个fixture的返回数据,fixture也可以返回一个元祖,list或字典,然后从里面取出对应数据。

@pytest.fixture()
def creat_id():
    return {
     'a':2*3,'b':2+3}
    
def test_compute(creat_id):
    some=creat_id['a']+4
    some2 = creat_id['b'] + 4
    assert some==11
    assert some2 == 11

执行用例,可以看到creat_id得到一个字典,然后再分别取值

creat_id = {
     'a': 6, 'b': 5}

    def test_compute(creat_id):
        some=creat_id['a']+4
        some2 = creat_id['b'] + 4
>       assert some==11
E       assert 10 == 11
test_study.py:41: AssertionError

2.也可以分成多个fixture,然后在用例中传多个fixture参数

@pytest.fixture()
def creat_id():
    return 2*3

@pytest.fixture()
def creat_id2():
    return 2+3

def test_compute(creat_id,creat_id2):
    some=creat_id+4
    some2 = creat_id2 + 4
    assert some==11
    assert some2 == 11

执行用例,可以看到creat_id = 6, creat_id2 = 5

creat_id = 6, creat_id2 = 5

    def test_compute(creat_id,creat_id2):
        some=creat_id+4
        some2 = creat_id2 + 4
>       assert some==11
E       assert 10 == 11
test_study.py:46: AssertionError

三,fixture互相调用

将定义为fixture的方法的返回值用在其他方法中

四,fixture的作用范围

fixture里面有个scope参数可以控制fixture的作用范围:

@pytest.fixture(scope='function')

session>module>class>function
-function:每一个函数或方法都会调用,默认情况下是function
-class:每一个类调用一次,一个类中可以有多个方法
-module:每一个.py文件调用一次,该文件内又有多个function和class
-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

注:
fixture为session级别是可以跨.py模块调用的,也就是当我们有多个.py文件的用例的时候,如果多个用例只需调用一次fixture,那就可以设置为scope=“session”,并且写到conftest.py文件里。
conftest.py文件名称时固定的,pytest会自动识别该文件。放到项目的根目录下就可以全局调用了,如果放到某个package下,那就在该ackage内有效
pytest之fixture的使用_第1张图片
scope越大,实例化越早
1.当函数调用多个fixtures的时候,scope较大的(比如session)实例化早于scope较小的(比如function或者class)
2.同样scope的顺序则按照其在测试函数中定义的顺序及依赖关系来实例化

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_“然后使用”@pytest.fixture(name=’’)"。

五,调用fixture的四种方法

1.函数或类里面方法直接传fixture的函数参数名称@pytest.fixture()

@pytest.fixture()
def test1():
    print('\n开始执行function')
    
def test_a(test1):
    print('---用例a执行---')

#执行结果
开始执行function
---用例a执行---

2.使用装饰器@pytest.mark.usefixtures()修饰需要运行的用例,该方式获取不到fixture标识函数的返回值
如:@pytest.mark.usefixtures('test1')

@pytest.fixture()
def test1():
    print('\n开始执行function')
    
@pytest.mark.usefixtures('test1')
def test_a():
    print('---用例a执行---')

#执行结果
开始执行function
---用例a执行---

usefixtures与传fixture区别
usefixture,无法获取到返回值。传fixture,可以获取到返回值。
所以当fixture需要用到return出来的参数时,只能使用传fixture方式,当不需要用到return出来的参数时,两种方式都可以。

3.叠加usefixtures
如果一个方法或者一个class用例想要同时调用多个fixture,可以使用
@pytest.mark.usefixtures()进行叠加。
注意叠加顺序,先执行的放底层,后执行的放上层。
如:先执行test2 再执行test1

@pytest.mark.usefixtures('test1')
@pytest.mark.usefixtures('test2')
def test_xx():   #测试用例
    pass

4.autouse设置为True,自动调用fixture功能
设置scope为module级别,在当前.py用例模块只执行一次,autouse=True自动使用
设置scope为function级别,每个用例前都调用一次,自动使用
如:@pytest.fixture(scope="module", autouse=True)

六,fixture使用yield实现setup和teardown

可以通过使用yield语句代替return,fixture里面的teardown用yield来唤醒teardown的执行,yield语句之后的所有代码都将用作拆卸代码

@pytest.fixture(scope="module")
def open():
    print("\n1,打开浏览器,并且打开百度首页")  #实现setup
    yield  10  #“yield 10”替代“return 10”,且实现teardown
    print("\n2,执行teardown!")
    print("3,最后关闭浏览器")

def test_compute2(open):
    assert open==10

def test_compute3(open):
    assert open==9

执行时加上“-s -q”命令,可以发现,scope="module"代表每个模块调用一次,调用时先执行open中的第一行打印代码,然后yield 10返回数值9给测试函数使用,当两个测试函数都执行完毕后(一个成功一个失败),再执行yield后面的两行打印代码

1,打开浏览器,并且打开百度首页
.F
2,执行teardown!
3,最后关闭浏览器

yield遇到异常:
1.如果其中一个用例出现异常,不影响yield后面的teardown执行,运行结果互不影响,并且在用例全部执行完之后,会呼唤teardown的内容
2.如果在setup就异常了,那么是不会去执行yield后面的teardown内容了

七,使用带有参数化fixture

与在@ pytest.mark.parametrize中使用的方式相同,用于在参数化灯具的值集中应用标记。在fixture中使用params参数
是一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它。如:
@pytest.fixture(params=[0, 1)
然后被fixture标记的函数需要使用request中的request.param才能使用到params中的值
1,

@pytest.fixture(params=[3,4,5, pytest.param(2, marks=pytest.mark.skip)])
def creat_id(request):
    print('打印param的值',request.param)
    return request.param+3

def test_compute(creat_id):
    assert creat_id==6

执行时加‘-v’命令,可以看到每次测试函数的情况,
运行此测试将跳过对creat_id函数中value 的调用“2”,最后test_compute一共执行了3次,分别使用了creat_id函数的三个值,每次得到一个参数值;跳过1条

test_study.py::test_compute[3] 打印param的值 3
PASSED
test_study.py::test_compute[4] 打印param的值 4
FAILED
test_study.py::test_compute[5] 打印param的值 5
FAILED
test_study.py::test_compute[2] SKIPPED

六,在各个级别上覆盖fixture

1.一个工程下可以建多个conftest.py的文件,一般在工程根目录下设置的conftest文件起到全局作用。在不同子目录下也可以放conftest.py的文件,作用范围只能在该层级以及以下目录生效。
2.conftest在不同的层级间的作用域不一样,再执行某模块时,先执行这个模块对应的祖先目录是否有conftest文件,然后依次往下级目录查找
pytest之fixture的使用_第2张图片
3.conftest是不能跨模块调用的
4.conftest.py与运行的用例要在同一个pakage下,并且有init.py文件
注意:
Pytest对于每个fixture只会缓存一个实例,这意味着如果使用参数化的fixture,pytest可能会比定义的作用域更多次的调用fixture函数(因为需要创建不同参数的fixture)

可以使用pytest --fixtures xx.py 来查看可用的fixture
在相对较大的测试套件中,很可能需要override一个global或root一个已locally 定义的夹具,以保持测试代码的可读性和可维护性。

1,在文件夹级别(通过conftest文件)重写fixtures方法
如在/test1/conftest.py/test1/test2/conftest.py中都有一个被fixture标识的同名方法:
/test1/路径下的测试模块使用conftest.py中的的fixture方法,
/test1/test2/路径下的测试模块会使用fixture.py文件中重写的fixture方法

test1/
    __init__.py
    conftest.py
       #test1/ conftest.py
        import pytest      
        @pytest.fixture
        def username():
            return 'username'
    test_something.py
        # test1/test_something.py
        def test_username(username):
            assert username == 'username'

    test2/
        __init__.py
        conftest.py
            #  test1/test2/conftest.py
            import pytest
            @pytest.fixture
            def username(username):
                return 'overridden-' + username
        test_something.py
            # test1/test2/test_something.py
            def test_username(username):
                assert username == 'overridden-username'

2,在测试模块级别重写fixtures方法
如在/test1/conftest.py都有一个被fixture标识的同名方法:username()
/test1路径下有一测试模块test_something.py,重写了fixtures方法1:username()
/test1路径下有一测试模块test_something_else.py,重写了fixtures方法2:username()
这两个文件分别使用自己的fixtures方法:username()

test1/
    __init__.py
    conftest.py
        # test1/conftest.py
        import pytest
        @pytest.fixture
        def username():
            return 'username'
            
    test_something.py
        # test1/test_something.py
        import pytest
        @pytest.fixture
        def username(username):
            return 'overridden-' + username
        def test_username(username):
            assert username == 'overridden-username'

    test_something_else.py
        # test1/test_something_else.py
        import pytest
        @pytest.fixture
        def username(username):
            return 'overridden-else-' + username
        def test_username(username):
            assert username == 'overridden-else-username'

总结:
fixtures方法的查找方法
先从根目录下的fixture.py文件中查找,
依次往次级目录的fixture.py文件中查找,
最后再查找当前测试模块py文件中是否有该fixtures方法,使用最近的一个fixtures方法
注:同一个py文件中可以有同名的fixtures方法,也是使用最近的一个

参考地址:https://www.cnblogs.com/huizaia/p/10331469.html
传入参数参考地址:https://blog.csdn.net/BearStarX/article/details/101197886

你可能感兴趣的:(pytest)