Pytest:Fixture参数

发布时间 2023-12-01 14:44:41作者: luoqingqing6

Fixture作用域之scope

用于控制Fixture的作用范围,作用类似于Pytest的setup/teardown
scope参数可以是session, module,class,function,默认为function,作用范围顺序:session》module》class》function

名称 范围 说明
function 函数级 每一个函数或方法都会调用
class 类级别 每个测试类只运行一次
module 模块级 每一个.py文件调用一次,scope="module" 可以实现多个.py 跨文件共享前置
session 会话级 每次会话只需要运行一次,会话内所有方法及类,模块都共享这个方法,scope="session" 以实现多个.py 跨文件使用一个 session 来完成多个用例
 

在使用之前我们先来了解什么是conftest.py?

实现测试用例的过程中,当你发现需要使用来自多个文件的fixture函数的时候,可以将这些fixture函数放到conftest.py中。

你不需要导入这些fixture函数,它会由pytest自动检索。

fixture函数的检索顺序是从测试类开始,然后测试的模块,然后就是conftest.py文件,最后是内置的插件和第三方插件。

scope="session"

session的作用范围是针对.py级别的,module是对当前.py生效,seesion是对多个.py文件生效
session只作用于一个.py文件时,作用相当于module
所以session多数与contest.py文件一起使用,做为全局Fixture

#conftest.py,这个名称是固定的不能修改
import pytest

@pytest.fixture(scope="session", autouse="True")
def login():
    print('所有接口都要用到登录token')
    username = "zhangsan"
    password = "123456"
    #相当于return,但是return不会执行后面的语句,yield可以
    yield username,password
    print("完成登录操作")
#用例文件test.py
import pytest

def test_one(login):
    print("接口需要登录token")
    username,passwd =login
    print(f"用户名:{username},用户密码:{passwd}")
if __name__ == '__main__':
    pytest.main()

============================= test session starts =============================
collecting ... collected 1 item

test1.py::test_one

============================== 1 passed in 0.09s ==============================
所有接口都要用到登录token
PASSED [100%]接口需要登录token
用户名:zhangsan,用户密码:123456
完成登录操作

return 与 yield的区别:

  • return:在程序函数中返回某个值,返回之后函数不在继续执行,彻底结束。
  • yield: 带有yield的函数是一个迭代器,函数返回某个值时,会停留在某个位置,返回函数值后,会在前面停留的位置继续执行直到程序结束

scope = “class”:

当测试类内的每一个测试方法都调用了fixture,fixture只在该class下所有测试用例执行前执行一次

测试类下面只有一些测试方法使用了fixture函数名,这样的话,fixture只在该class下第一个使用fixture函数的测试用例位置开始算,后面所有的测试用例执行前只执行一次。而该位置之前的测试用例就不管。

@pytest.fixture(scope='class')
def login():
    print("输入账号密码登录")
    username = "zhangsan"
    passwd = "123456"
    return username,passwd

class Test_Class():
    def test_one(self):
        print("测试用例前置条件不需要登录")

    def test_two(self,login):
        print("测试用例前置条件需要登录,返回值为{}".format(login))

    def test_three(self,login):
        print("测试用例前置条件需要登录,返回值为{}".format(login))

if __name__ == '__main__':
    pytest.main()

============================= test session starts =============================
collecting ... collected 3 items


test1.py::Test_Class::test_one
test1.py::Test_Class::test_two
test1.py::Test_Class::test_three


============================== 3 passed in 0.10s ==============================
PASSED [ 33%]测试用例前置条件不需要登录
输入账号密码登录
PASSED [ 66%]测试用例前置条件需要登录,返回值为('zhangsan', '123456')
PASSED [100%]测试用例前置条件需要登录,返回值为('zhangsan', '123456')


scope = “module”:

与class相同,只从.py文件开始引用fixture的位置生效

@pytest.fixture(scope='module')
def login():
    print("输入账号密码登录")
    username = "zhangsan"
    passwd = "123456"
    return username,passwd

def test_four(login):
    print("测试用例4前置条件需要登录,返回值为{}".format(login))

class Test_Class():
    def test_one(self):
        print("测试用例1前置条件需要登录,返回值为{}".format(login))

    def test_two(self):
        print("测试用例2前置条件需要登录,返回值为{}".format(login))

    def test_three(self):
        print("测试用例3前置条件需要登录,返回值为{}".format(login))

if __name__ == '__main__':
    pytest.main()

============================= test session starts =============================
collecting ... collected 4 items

test1.py::test_four
test1.py::Test_Class::test_one
test1.py::Test_Class::test_two
test1.py::Test_Class::test_three

============================== 4 passed in 0.09s ==============================
输入账号密码登录
PASSED [ 25%]测试用例4前置条件需要登录,返回值为('zhangsan', '123456')
PASSED [ 50%]测试用例1前置条件需要登录,返回值为<function login at 0x00000263300FCAE0>
PASSED [ 75%]测试用例2前置条件需要登录,返回值为<function login at 0x00000263300FCAE0>
PASSED [100%]测试用例3前置条件需要登录,返回值为<function login at 0x00000263300FCAE0>

autouse:

默认False,若为True,刚每个测试函数都会自动调用该fixture,无需传入fixture函数名
由此我们可以总结出调用fixture的三种方式:
1、函数或类里面方法直接传fixture的函数参数名称
2、使用装饰器@pytest.mark.usefixtures()修饰
3、autouse=True自动调用,无需传仍何参数,作用范围跟着scope走(谨慎使用)

@pytest.fixture(scope='function',autouse="True")
def login():
    print("输入账号密码登录")
    username = "zhangsan"
    passwd = "123456"
    return username,passwd

def test_one1():
    print("测试用例1前置条件需要登录")

def test_two2():
    print("测试用例1前置条件需要登录")

def test_three3():
    print("测试用例1前置条件需要登录")

def test_four4():
    print("测试用例1前置条件需要登录")

============================= test session starts =============================
collecting ... collected 4 items

test1.py::test_one1
test1.py::test_two2
test1.py::test_three3
test1.py::test_four4

============================== 4 passed in 0.10s ==============================
输入账号密码登录
PASSED [ 25%]测试用例1前置条件需要登录
输入账号密码登录
PASSED [ 50%]测试用例1前置条件需要登录
输入账号密码登录
PASSED [ 75%]测试用例1前置条件需要登录
输入账号密码登录
PASSED [100%]测试用例1前置条件需要登录v

fixture参数化之params

Fixture的可选形参列表,支持列表传入,默认None,每个param的值,fixture都会去调用执行一次,类似for循环,可与参数ids一起使用,作为每个参数的标识,详见ids

使用方法

在fixture方法上加装饰器@pytest.fixture(params=[1,2,3]),就会传入三个数据1,2,3,分别将这三个数据传入到用例当中。传入的数据需要使用一个固定的参数名request来接收,

被Fixture装饰的函数要调用是采用:Request.param(固定写法,如下图)

#conftest.py
import pytest
@pytest.fixture(scope="session", params=["zhangsan",123456])
def login(request):
    print(f'所有接口都要用到登录token,{request.param}')
    yield request.param
    print("完成登录操作")

@pytest.fixture(scope="session", params=[["zhangsan",123456],["lisi",123456]])
def login(request):
print(f'所有接口都要用到登录token,{request.param}')
yield request.param
print("完成登录操作")
#test.py
import pytest def test_get_user(login): print("接口需要登录token") print(f"用户信息{login}")

============================= test session starts =============================
collecting ... collected 2 items

test1.py::test_get_user[zhangsan]
test1.py::test_get_user[123456]

============================== 2 passed in 0.09s ==============================
所有接口都要用到登录token,zhangsan
PASSED [ 50%]接口需要登录token
用户信息zhangsan
完成登录操作
所有接口都要用到登录token,123456
PASSED [100%]接口需要登录token
用户信息123456
完成登录操作

============================= test session starts =============================
collecting ... collected 2 items

test1.py::test_get_user[login0]
test1.py::test_get_user[login1]

============================== 2 passed in 0.10s ==============================
所有接口都要用到登录token,['zhangsan', 123456]
PASSED [ 50%]接口需要登录token
用户信息['zhangsan', 123456]
完成登录操作
所有接口都要用到登录token,['lisi', 123456]
PASSED [100%]接口需要登录token
用户信息['lisi', 123456]
完成登录操作

ids:用例标识ID

与params配合使用,一对一关系

import pytest
@pytest.fixture(scope="session", params=[["zhangsan",123456],["lisi",123456]],ids=["one","two"])
def login(request):
    print(f'所有接口都要用到登录token,{request.param}')
    yield request.param
    print("完成登录操作")

def test_get_user(login):
    print("接口需要登录token")
    print(f"用户信息{login}")

fixturehe和@pytest.mark.parametrize结合的参数化

#conftest.py
import pytes
@pytest.fixture()
def fixturefun(request):
    test = request.param
    print(test)
    return test
#test.py
import pytest

test_param = [(1,1),("2","2"),(False,False),(int,int)]
@pytest.mark.parametrize("fixturefun",test_param,indirect=True)
# indirect=True 声明fixturefun不是参数,而是一个函数
def test_001(fixturefun):
    assert fixturefun[0] == fixturefun[1]
if __name__ == '__main__':
pytest.main()

============================= test session starts =============================
collecting ... collected 4 items

test1.py::test_001[fixturefun0]
test1.py::test_001[fixturefun1]
test1.py::test_001[fixturefun2]
test1.py::test_001[fixturefun3]

============================== 4 passed in 0.09s ==============================
(1, 1)
PASSED [ 25%]('2', '2')
PASSED [ 50%](False, False)
PASSED [ 75%](<class 'int'>, <class 'int'>)
PASSED [100%]

fixture重命名之name

通常来说使用 fixture 的测试函数会将 fixture 的函数名作为参数传递,但是 pytest 也允许将fixture重命名,如果使用了name,那只能将name传如,函数名不再生效
在fixture方法上加装饰器@pytest.fixture(name=‘XXX’)