Pytest series - detailed use of fixtures

foreword

  • The previous article said that setup and teardown can add some operations before or after the execution of the use case, but this is globally effective for the entire script
  • If there are the following scenarios: use case 1 needs to log in first, use case 2 does not need to log in, and use case 3 needs to log in first. Obviously it can't be achieved with setup and teardown
  • Fixture allows us to customize the preconditions of test cases 

Advantages of fixtures

  • The naming method is flexible, not limited to the names of setup and teardown
  • Data sharing can be realized in conftest.py configuration, and fixtures can be found automatically without import
  • scope="module" can realize multiple .py cross-file sharing front
  • scope="session" to achieve multiple .py cross-file use a session to complete multiple use cases

fixture parameter list

@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None)
def test():
    print("fixture初始化的参数列表")

parameter list

  • scope: can be understood as the scope of fixture:
    • Default: function, there are four classes, modules, packages, sessions [commonly used]
  • autouse:
    • Default: False, the use case needs to manually call the fixture;
    • If True, all scoped test cases will automatically call this fixture
  • name: default: the name of the decorator, the fixtures of the same module call each other, it is recommended to write a different name

Notice

The scope of session: is the entire test session, that is, from the start of pytest to the end of the test

How the test case calls the fixture

  1. Use the fixture name as the input parameter of the test case function
  2. Test case plus decorator: @pytest.mark.usefixtures(fixture_name)
  3. Fixture set autouse=True
import pytest

# 调用方式一
@pytest.fixture
def login():
    print("输入账号,密码先登录")


def test_s1(login):
    print("用例 1:登录之后其它动作 111")


def test_s2():  # 不传 login
    print("用例 2:不需要登录,操作 222")


# 调用方式二
@pytest.fixture
def login2():
    print("please输入账号,密码先登录")


@pytest.mark.usefixtures("login2", "login")
def test_s11():
    print("用例 11:登录之后其它动作 111")


# 调用方式三
@pytest.fixture(autouse=True)
def login3():
    print("====auto===")


# 不是test开头,加了装饰器也不会执行fixture
@pytest.mark.usefixtures("login2")
def loginss():
    print(123)

Results of the

knowledge points

  • Add above the class declaration 
    • @pytest.mark.usefixtures() , which means that all test cases in this class will call this fixture
  • Can stack multiple: 
    • @pytest.mark.usefixtures() , put the ones executed first on the bottom layer, and the ones executed later on the upper layer
  • You can pass multiple fixture parameters, put the one executed first in the front, and the one executed later in the back
  • If the fixture has a return value, the return value cannot be obtained with @pytest.mark.usefixtures(), and the method of passing parameters must be used (method 1)

Fixture instantiation order

  • A fixture (session) in a higher scope is a fixture (function, class) in a lower scope
    • Instantiate before [session > package > module > class > function]
  • Fixtures with the same scope follow the order declared in the test function, and follow the dependencies between fixtures [the dependent fixture_B in fixture_A is instantiated first, and then instantiated in fixture_A]
  • Fixtures that are automatically used (autouse=True) will be instantiated before fixtures that are explicitly used (passing parameters or decorators)
import pytest

order = []

@pytest.fixture(scope="session")
def s1():
    order.append("s1")


@pytest.fixture(scope="module")
def m1():
    order.append("m1")


@pytest.fixture
def f1(f3, a1):
    # 先实例化f3, 再实例化a1, 最后实例化f1
    order.append("f1")
    assert f3 == 123


@pytest.fixture
def f3():
    order.append("f3")
    a = 123
    yield a


@pytest.fixture
def a1():
    order.append("a1")


@pytest.fixture
def f2():
    order.append("f2")


def test_order(f1, m1, f2, s1):
    # m1、s1在f1后,但因为scope范围大,所以会优先实例化
    assert order == ["s1", "m1", "f3", "a1", "f1", "f2"]

Results of the

Assertion success

Points to note about fixtures

Added @pytest.fixture, if the fixture wants to rely on other fixtures, you need to use the function to pass parameters instead of @pytest.mark.usefixtures(), otherwise it will not take effect

 What I said earlier is actually the operation of setup, so now let’s talk about how teardown is implemented

import pytest


@pytest.fixture(scope="session")
def open():
    # 会话前置操作setup
    print("===打开浏览器===")
    test = "测试变量是否返回"
    yield test
    # 会话后置操作teardown
    print("==关闭浏览器==")


@pytest.fixture
def login(open):
    # 方法级别前置操作setup
    print(f"输入账号,密码先登录{open}")
    name = "==我是账号=="
    pwd = "==我是密码=="
    age = "==我是年龄=="
    # 返回变量
    yield name, pwd, age
    # 方法级别后置操作teardown
    print("登录成功")


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


def test_s2(login):
    print("==用例2==")
    print(login)

 

Yield Notes

  • If the code before yield, that is, the setup part has thrown an exception, the teardown content after yield will not be executed
  • If the test case throws an exception, the teardown content after yield will still be executed normally

 

The combination of yield+with


# 官方例子
@pytest.fixture(scope="module")
def smtp_connection():
    with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
        yield smtp_connection  # provide the fixture value

The smtp_connection connection will be closed after the test finishes executing, because the smtp_connection object is automatically closed when the with statement ends.

addfinalizer Finalizer function


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

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

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


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

Precautions

  • If the code before request.addfinalizer(), that is, the setup part has thrown an exception, the teardown content of request.addfinalizer() will not be executed (similar to yield, it should be changed to be consistent in the latest new version)
  • You can declare multiple finalizers and call

Guess you like

Origin blog.csdn.net/qq_41663420/article/details/129798309