conftest文件详解

  • conftest的作用
  • yield实现teardown
  • yield+with的结合
  • addfinalizer终结函数


conftest的作用

首先, conftest.py 的文件名称是固定的, pytest 会自动识别该文件,我们可以理解成一个专门存放 fixture 的配置文件。

一个工程下可以建多个 conftest.py 文件,一般我们都是在工程根目录下设置的 conftest 文件,这样会起到一个全局的作用。 我们也可以在不同的子目录下放 conftest.py ,这样作用范围只能在该层级的子目录下生效。

conftest.py 配置 fixture 注意事项:

  • pytest 会默认读取 conftest.py 里面的所有 fixture。
  • conftest.py 文件名称是固定的,不能改动。
  • conftest.py 只对同一个 package 下的所有测试用例生效。
  • 不同目录可以有自己的 conftest.py,一个项目中可以有多个 conftest.py。
  • 测试用例文件中不需要手动 import conftest.py,pytest 会自动查找。

  • 下面我们来创建一个 conftest.py 的文件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time   : 2021/11/22 13:23
# @Author : 余少琪
import pytest


"""
scope:
    1、scope 默认是function,只要是函数都会运行
    2、class 类级别,每个函数都会运行一次
    3、session 整个作用域只运行一次
    4、module 整个py文件(模块)作用域只运行一次
    
autouse: 是否自动调用

"""


@pytest.fixture(scope='session', autouse=True)
def login_init():
    print("这里返回了一个token")
  • 然后在存放 conftest.py 文件的根目录下在创建一个 test_demo.py 文件
import pytest


def test_aaa():
    print("这里是aaa函数")


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

我们来查看一下执行结果:
pytest在执行 test_demo.py 文件之前,会先执行 conftest.py 文件中的方法,然后在执行 test_demo.py 文件。

============================= test session starts =============================
platform win32 -- Python 3.8.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: C:\Users\hzxy\PycharmProjects\pytest_api_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45
collected 1 item

test_demo.py 这里返回了一个token
这里是aaa函数
.

============================== 1 passed in 0.02s ==============================

yield实现teardown

fixture 实现 teardown 并不是一个独立的函数,而是用 yield 关键字来开启 teardown 操作。

pytest.fixture(scope="session") 时,作用域是整个测试会话,即开始执行pytest 到结束测试只会执行一次。

pytest.fixture(scope="function") 时,pytestyield 类似 unittestteardown 。每个方法(函数)都会执行一次。

pytest.fixture(scope="module") 时, module 作用是整个 .py 文件都会生效(整个文件只会执行一次),用例调用时,参数写上函数名称就可以。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time   : 2021/11/25 10:50
# @Author : 余少琪
import pytest


import pytest


@pytest.fixture(scope="session")
def open():
    # 会话前置操作setup
    print("===开始执行测试用例===")
    yield
    # 会话后置操作teardown
    print("===测试用例执行完成===")


@pytest.fixture
def login(open):
    # 方法级别前置操作setup
    print("===登陆操作login===")
    name = "===账号==="
    pwd = "===密码==="
    # 返回变量
    yield name, pwd
    # 方法级别后置操作teardown
    print("===登录成功login===")


def test_case1(login):
    print("===测试用例1===")
    # 返回的是一个元组
    print(login)
    # 分别赋值给不同变量
    name, pwd = login
    print(name, pwd)
    assert "账号" in name
    assert "密码" in pwd


def test_case2(login):
    print("===测试用例2===")
    print(login)


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

我们来查看一下执行结果:

============================= test session starts =============================
platform win32 -- Python 3.8.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: C:\Users\hzxy\PycharmProjects\pytest_api_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45
collected 2 items

test_demo.py 这里返回了一个token
===开始执行测试用例===
===登陆操作login===
===测试用例1===
('===账号===', '===密码===')
===账号=== ===密码===
.===登录成功login===
===登陆操作login===
===测试用例2===
('===账号===', '===密码===')
.===登录成功login===
===测试用例执行完成===


============================== 2 passed in 0.03s ==============================

注意

  • 如果 yield 前面的代码,即 setup 部分已经抛出异常,则不会执行 yield 后面的 teardown 内容。
  • 如果测试用例抛出异常,yield 后面的 teardown 内容还是会正常执行。

yield+with的结合

yield 也可以配合 with 语句使用。

import pytest
import smtplib

@pytest.fixture(scope="module")
def smtp_connection():
    with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
    	# 在 yield 后面添加smtp_connection,则类似 return,返回 smtp_connection信息
        yield smtp_connection

addfinalizer终结函数

除了 yield 可以实现 teardown ,在 request-context 对象中注册 addfinalizer 方法也可以实现终结函数。

在用法上, addfinalizeryield 是不同的,需要你去注册作为终结器使用的函数。例如:增加一个函数 fin ,并且注册成终结函数。

import pytest


@pytest.fixture(scope="module")
def test_addfinalizer(request):
    # 前置操作setup
    print("===打开浏览器===")
    test = "test_addfinalizer"


    def fin():
        # 后置操作teardown
        print("===关闭浏览器===")

    request.addfinalizer(fin)
    # 返回前置操作的变量
    return test


def test_case(test_addfinalizer):
    print("===最新用例===", test_addfinalizer)

返回结果:

============================= test session starts =============================
platform win32 -- Python 3.8.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: C:\Users\hzxy\PycharmProjects\pytest_api_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45
collected 1 item

test_demo.py 这里返回了一个token
===打开浏览器===
===最新用例=== test_addfinalizer
.===关闭浏览器===


============================== 1 passed in 0.02s ==============================

yield 与 addfinalizer 的区别:
addfinalizer 可以注册多个终结函数。

import pytest


@pytest.fixture()
def demo_addfinalizer(request):
    print("====setup====")

    def fin1():
        print("====teardown1====")

    def fin2():
        print("====teardown2====")

    def fin3():
        print("====teardown3====")

    # 注册demo_addfinalizer为终结函数
    request.addfinalizer(fin1)
    request.addfinalizer(fin2)
    request.addfinalizer(fin3)


def test_case1(demo_addfinalizer):
    print("====执行用例test_case1====")


def test_case2(demo_addfinalizer):
    print("====执行用例test_case2====")


def test_case3(demo_addfinalizer):
    print("====执行用例test_case3====")


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

返回结果:

============================= test session starts =============================
platform win32 -- Python 3.8.8, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: C:\Users\hzxy\PycharmProjects\pytest_api_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45
collected 3 items

test_demo.py 这里返回了一个token
====setup====
====执行用例test_case1====
.====teardown3====
====teardown2====
====teardown1====
====setup====
====执行用例test_case2====
.====teardown3====
====teardown2====
====teardown1====
====setup====
====执行用例test_case3====
.====teardown3====
====teardown2====
====teardown1====


============================== 3 passed in 0.04s ==============================