pytest fixture及conftest详解二 (pytest fixture用法)

在这里插入图片描述


一、pytest fixture一些概念

1、pytest fixture 说明

fixture是在测试函数运行前后,由pytest执行的外壳函数。fixture中的代码可以定制,满足多变的测试需求,包括定义传入测试中的数据集、配置测试前系统的初始状态、为批量测试提供数据源等等。fixture是pytest的精髓所在,类似unittest中setup/teardown,但是比它们要强大、灵活很多,它的优势是可以跨文件共享。

2、pytest fixture几个关键特性

a、独立命名,可以通过方法(function)、类(class)、模块(module)、整个项目(session)来激活
b、按照模块化的方式实现,每个fixture都可以相互调用
c、fixture可以实现unittest不能实现的功能,比如unittest中的测试用例和测试用例之间无法传递参数和数据,但是fixture可以解决这个问题
d、fixture的范围从简单的单元拓展到复杂的接口\UI测试,允许根据配置和组件选项对fixture和测试用例进行参数化

3、pytest fixture定义

a、定义fixture和定义普通函数差不多,唯一的区别就是在函数上加个装饰器@pytest.fixture(),fixture命名不要用test_开头,跟用例区分开.
b、fixture装饰器里的scope有4个级别的参数:function、class、module、session;
c、fixture可以有返回值,如果没有return,默认返回None;用例调用fixture的返回值,就是直接把fixture的函数名称作为参数传入
d、fixture可以返回一个元组\列表\字典
e、测试用例可以传单个、多个fixture参数
f、fixture和fixture间可以相互调用

二、Pytest fixture用法

用法一:作为参数使用

1.将fixture函数作为参数传递给测试用例

fixture的名字直接作为测试用例的参数,用例调用fixture的返回值,直接将fixture的函数名称当做变量名称;如果用例需要用到多个fixture的返回数据,fixture也可以返回一个元组,列表或字典,然后从里面取出对应数据。

conftest.py

import pytest

data=[{'user':'kobe','pwd':666666},
      {'user':'curry','pwd':666666},
      {'user':'james','pwd':666666},]

@pytest.fixture(scope='function',params=data,ids=['data1','data2','data3'],name='fix')
def my_fixture(request):
    print('这是前置方法')
    yield request.param
    print('这是后置方法')

test_demo1.py

import pytest

class TestDemo:

    def test_01_work(self,fix):
        print('执行测试用例1,',fix)
        assert fix['user']=='kobe' and fix['pwd']==666666

    def test_02_work(self):
        print('执行测试用例2')


class TestDemo1:

    def test_03_work(self):
        print('执行测试用例3')

    def test_04_work(self):
        print('执行测试用例4')



if __name__ == '__main__':
    pytest.main(['-vs','./test_demo1.py'])

2.同一个用例中传入多个fixture函数

conftest.py

import pytest


@pytest.fixture(scope='function',autouse=True)
def my_fixture():
    print('这是前置方法')
    yield
    print('这是后置方法')


@pytest.fixture(scope='function')
def my_fixture1():
    user='kobe'
    yield user


@pytest.fixture(scope='function')
def my_fixture2():
    pwd=666666
    yield pwd


test_run1.py

import pytest


class TestRun:

    def test_01_run(self,my_fixture1,my_fixture2):
        print('执行测试用例1')
        user=my_fixture1
        pwd=my_fixture2
        print('参数为:', user, pwd)

    def test_02_run(self):
        print('执行测试用例2',)

if __name__ == '__main__':
    pytest.main(['-vs','./test_run1.py'])

执行结果:

test_run1.py::TestRun::test_01_run 这是前置方法
执行测试用例1
参数为: kobe 666666
PASSED这是后置方法

test_run1.py::TestRun::test_02_run 这是前置方法
执行测试用例2
PASSED这是后置方法


============================== 2 passed in 0.10s ==============================

Process finished with exit code 0

3.fixture函数之间的相互传递

conftest.py

import pytest


@pytest.fixture(scope='function', autouse=True)
def my_fixture():
    print('这是前置方法')
    yield
    print('这是后置方法')


@pytest.fixture(scope='function')
def my_fixture1():
    print('获取用户名和密码')
    user = 'kobe'
    pwd = 666666
    yield user, pwd


@pytest.fixture(scope='function')
def my_fixture2(my_fixture1):
    print('登录操作,获取token')
    user=my_fixture1[0]
    pwd=my_fixture1[1]
    token = 上面获取大的值
    yield token

test_run1.py

import pytest


class TestRun:

    def test_01_run(self,my_fixture2):
        print('执行测试用例1')
        token=my_fixture2
        print('参数为:', token)

    def test_02_run(self):
        print('执行测试用例2',)

if __name__ == '__main__':
    pytest.main(['-vs','./test_run1.py'])

执行结果

test_run1.py::TestRun::test_01_run 这是前置方法
获取用户名和密码
登录操作,获取token
执行测试用例1
参数为: rthfgsdsgfgtynfgsxbvbm
PASSED这是后置方法

test_run1.py::TestRun::test_02_run 这是前置方法
执行测试用例2
PASSED这是后置方法


============================== 2 passed in 0.09s ==============================

Process finished with exit code 0

用法二:提供灵活的类似setup和teardown功能

a.Pytest fixture另一个强大的功能就是在函数执行前后增加操作,类似setup和teardown操作,但是比setup和teardown的操作更加灵活;具体使用方式是同样定义一个函数,然后用装饰器标记为fixture,然后在此函数中使用一个yield语句,yield语句之前的就会在测试用例之前使用,yield之后的语句就会在测试用例执行完成之后再执行。

案例

@pytest.fixture()
def fixture_driver():
    driver = webdriver.Chrome()
    yield driver
    driver.quit()
 
def test_baidu(fixture_driver):
    driver = fixture_driver
    driver.get("http://www.baidu.com")
    el=driver.find_element_by_xpath('//a[text()='新闻']')
    el.click()

用法三:利用pytest.mark.usefixtures叠加调用多个fixture

如果一个方法或者一个类中的用例想要同时调用多个fixture,可以使用@pytest.mark.usefixtures()进行叠加;
叠加顺序,先执行的放在底层,后执行的放在上层.
特别注意:
1.与直接传入的fixture不同的是,@pytest.mark.usefixtures无法获取到被fixture装饰的函数的返回值
2. @pytest.mark.usefixtures的使用场景是:被测试方法需要多个fixture做前后置工作时使用;

conftest.py

import pytest


@pytest.fixture(scope='function', autouse=False)
def my_fixture():
    print('这是前置方法1')
    yield
    print('这是后置方法1')


@pytest.fixture(scope='function',autouse=False)
def my_fixture1():
    print('这是前置方法2')
    yield
    print('这是后置方法2')


@pytest.fixture(scope='function',autouse=False)
def my_fixture2():
    print('这是前置方法3')
    yield
    print('这是后置方法3')

test_run1.py

import pytest


class TestRun:

    @pytest.mark.usefixtures('my_fixture2')
    @pytest.mark.usefixtures('my_fixture1')
    @pytest.mark.usefixtures('my_fixture')
    def test_01_run(self,):
        print('执行测试用例1' )

    def test_02_run(self):
        print('执行测试用例2')


if __name__ == '__main__':
    pytest.main(['-vs','./test_run1.py'])

执行结果

test_run1.py::TestRun::test_01_run 这是前置方法1
这是前置方法2
这是前置方法3
执行测试用例1
PASSED这是后置方法3
这是后置方法2
这是后置方法1

test_run1.py::TestRun::test_02_run 执行测试用例2
PASSED

============================== 2 passed in 0.09s ==============================

用法四:内置fixture函数之pytestconfig详解

pytestconfig 是pytest框架的一个内置fixture函数,可以获取上下文,它的作用跟 request.config 是一样的,代表pytest配置对象。

pytestconfig的源代码

@fixture(scope="session")
def pytestconfig(request: FixtureRequest) -> Config:
    """Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
 
    Example::
 
        def test_foo(pytestconfig):
            if pytestconfig.getoption("verbose") > 0:
                ...
 
    """
    return request.config

pytestconfig fixture函数中有2个比较常用的方法:

pytestconfig.getoption 获取命令行参数(对应的参数值):

不但可以获取pytest框架中自带的命令行参数值,也可以获取用户注册的自定义参数值
在这里插入图片描述

conftest.py

import pytest

@pytest.fixture(scope='session',autouse=True)
def my1_fixture():
    print('----这是前置方法----')
    yield
    print('----这是后置方法----')


# conftest.py
def pytest_addoption(parser):
    # parser.addoption():为pytest添加命令行参数
    parser.addoption("--cmdopt",
                     action="store",
                     default="默认参数值",
                     help="my option: type1 or type2")


@pytest.fixture()
def cmdopt(pytestconfig):
    return pytestconfig.getoption("--cmdopt")

test_run1.py

import pytest


class TestRun:

    def test_run_1(self,cmdopt):
        # 通过conftest.py文件中定义的fixture函数来获取自定义参数值,
        # 但是定义的fixture函数中原理依旧是调用了pytest内置fixture函数pytestconfig
        print("test_cmd_1当前获取的参数为:{}".format(cmdopt))

    def test_run_2(self,request):
        cmdopt = request.config.getoption("cmdopt")
        # 直接通过调用pytest内置fixture函数request获取自定义参数值
        print("test_cmd_2当前获取的参数为:{}".format(cmdopt))

    def test_run3_3(self,pytestconfig):
        cmdopt = pytestconfig.getoption("cmdopt")
        # 直接通过调用pytest内置fixture函数pytestconfig获取自定义参数值
        print("test_cmd_3当前获取的参数为:{}".format(cmdopt))

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

命令行执行参数:

D:\project_development\api_pytest>pytest -s --cmdopt=我和我的祖国

执行结果:

collected 3 items                                                                                                                  

testcases\users\test_run1.py ----这是前置方法----
test_cmd_1当前获取的参数为:我和我的祖国
.test_cmd_2当前获取的参数为:我和我的祖国
.test_cmd_3当前获取的参数为:我和我的祖国
.----这是后置方法----


======================================================== 3 passed in 0.13s ========================================================
pytestconfig.addini 动态添加pytest.ini配置文件中的参数

1、在添加自定义参数到pytest配置对象中:在conftest.py文件中通过钩子函数 pytest_addoption 中使用 addoption 方法来添加自定义命令行参数。
2、在conftest.py文件中通过钩子函数 pytest_addoption 中使用 addini 方法来添加pytest.ini配置文件中的参数。

addini 方法源代码

name:添加到pytest.ini配置文件中的参数名;
help:对参数名的帮助说明,方便查阅;
type:参数类型,默认None,可以设置:None, “pathlist”, “args”, “linelist”, “bool”;
default:参数默认值;

def addini(self, name, help, type=None, default=None):
        """ register an ini-file option.
        :name: name of the ini-variable
        :type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
               or ``bool``.
        :default: default value if no ini-file option exists but is queried.
        The value of ini-variables can be retrieved via a call to
        :py:func:`config.getini(name) <_pytest.config.Config.getini>`.
        """
        assert type in (None, "pathlist", "args", "linelist", "bool")
        self._inidict[name] = (help, type, default)
        self._ininames.append(name)

案例

conftest.py

import pytest

@pytest.fixture(scope='session',autouse=True)
def my1_fixture():
    print('----这是前置方法----')
    yield
    print('----这是后置方法----')



def pytest_addoption(parser):
    # 添加配置参数
    parser.addini(
        name="base_url",
        type=None,
        default="http://www.baidu.com",
        help="base_url配置参数"
    )


@pytest.fixture()
def base_url(pytestconfig):
    return pytestconfig.getini("base_url")

test_run1.py

import pytest


class TestRun:

    def test_run_1(self,base_url):
        print("当前获取到的base_url = {}".format(base_url))

    def test_run_2(self,base_url):
        print("当前获取到的base_url = {}".format(base_url))



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

命令行执行参数

pytest -s

执行结果

collected 2 items                                                                                                                  

testcases\users\test_run1.py ----这是前置方法----
当前获取到的base_url = http://www.baidu.com
.当前获取到的base_url = http://www.baidu.com
.----这是后置方法----


======================================================== 2 passed in 0.13s ========================================================

pytest.ini 配置 url地址

如果有一天我们的测试环境发生了改变,这时候不需要去改代码,只需在 pytest.ini 配置一个环境地址

特别注意:pytest.ini配置在根目录下

在这里插入图片描述

pytest.ini文件

[pytest]
markers =
    smoke: Run the smoke test functions for tasks project
    get: Run the test functions that test tasks.get()

url = https://blog.csdn.net/YZL40514131

conftest.py

import pytest

@pytest.fixture(scope='session',autouse=True)
def my1_fixture():
    print('----这是前置方法----')
    yield
    print('----这是后置方法----')



def pytest_addoption(parser):
    parser.addoption( "--cmdopt",
                      action="store",
                      default="type1",
                      help="my option: type1 or type2"
    )
    # 添加参数到pytest.ini
    parser.addini('url',
                  type=None,
                  default="http://www.baidu.com/",
                  help='添加 url 访问地址参数')


# 获取 pytest.ini 配置参数
@pytest.fixture(scope="session")
def home_url(pytestconfig):
    url = pytestconfig.getini('url')
    print("\n读取到配置文件的url地址:%s" % url)
    return url

test_work1.py

#encoding=utf-8

import pytest


class TestWork:

    def test_h(self,home_url):
        print("用例:%s" % home_url)

    def test_work1(self):
        assert 1 == 1

    def test_work2(self):
        assert 2 == 5


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

执行结果:
在这里插入图片描述

当我们使用pytest进行测试时,fixture和conftest是非常有用的工具。 Fixture是pytest中的一个装饰器,用于在测试用例执行前后做一些准备和收尾工作。比如说,在测试用例中需要使用数据库,那么我们可以使用fixture在测试用例执行前连接数据库,在测试用例执行后断开连接。这样可以避免在每个测试用例中都重复写相同的代码。 下面是一个使用fixture的例子: ```python import pytest @pytest.fixture def setup_database(): # 连接数据库 yield # 断开连接 ``` 在这个例子中,我们定义了一个名为setup_database的fixture,这个fixture在测试用例执行前连接数据库,在测试用例执行后断开连接。 conftestpytest提供的一个特殊的文件,用于存放fixture。在一个项目中,如果有多个测试用例需要使用同一个fixture,那么我们可以将这个fixture放到conftest.py中,这样所有的测试用例都可以使用这个fixture了。 下面是一个使用conftest的例子: 在conftest.py中定义一个setup_database的fixture: ```python import pytest @pytest.fixture def setup_database(): # 连接数据库 yield # 断开连接 ``` 在测试用例中使用这个fixture: ```python def test_something(setup_database): # 使用数据库进行测试 ``` 这样,我们就可以在测试用例中使用setup_database这个fixture了,不需要重复写连接和断开数据库的代码。 总之,fixture和conftestpytest中非常有用的工具,可以帮助我们在测试用例执行前后做一些准备和收尾工作,避免重复写相同的代码。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

敲代码敲到头发茂密

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值