Pytest 中 class 作用域夹具的意外行为

Unexpected behavior of class scoped fixture in Pytest

我正在学习 pytest 并研究不同夹具范围的行为。当我 运行 测试时,我看到 class 作用域固定装置的意外行为。这是我的项目结构。

Pytest_Basics
│   conftest.py
└───pack1
        test_a.py
        test_b.py

这是每个文件的内容。

conftest.py

import pytest


@pytest.fixture(scope='session', autouse=True)
def ses_fix():
    print('In session fixture')


@pytest.fixture(scope='package', autouse=True)
def pak_fix():
    print('In package fixture')


@pytest.fixture(scope='module', autouse=True)
def mod_fix():
    print('In module fixture')


@pytest.fixture(scope='class', autouse=True)
def cls_fix():
    print('In class fixture')


@pytest.fixture(scope='function', autouse=True)
def func_fix():
    print('In functon fixture')

test_a.py

class TestA:

    def test_a1(self):
        assert True

    def test_a2(self):
        assert True

    def test_a3(self):
        assert True

test_b.py

def test_b1():
    assert True


def test_b2():
    assert True


def test_b3():
    assert True

当我运行测试时使用pytest -v -s。我得到低于输出。

pack1/test_a.py::TestA::test_a1 In session fixture
In package fixture
In module fixture
In class fixture
In functon fixture
PASSED
pack1/test_a.py::TestA::test_a2 In functon fixture
PASSED
pack1/test_a.py::TestA::test_a3 In functon fixture
PASSED
pack1/test_b.py::test_b1 In module fixture        
In class fixture
In functon fixture
PASSED
pack1/test_b.py::test_b2 In class fixture
In functon fixture
PASSED
pack1/test_b.py::test_b3 In class fixture
In functon fixture
PASSED

我预计 class scoped fixture 只会 运行 一次,因为我在 test_a.py 模块中只有一个 class。但是,我在 test_b.py 模块中执行测试时看到它是 运行ning。

是什么导致了这种行为?这是一个错误还是我对 class 级装置的了解有限。

环境:Python - 3.9.5,Pytest - 6.2.4

这确实是 class 范围内装置的行为方式。 documentation中没有直接提到,但可以从中推导出来。如所述:

Fixtures are created when first requested by a test, and are destroyed based on their scope

具有 autouse=True 的夹具应用于会话中的每个测试函数。它们根据它们的范围被销毁,这意味着 基于会话、模块或函数的固定装置在每个测试函数所在的会话、模块或函数结束时被销毁。然而,基于 Class 的固定装置可以在 classes 之外调用,并且为了保持一致,在这种情况下,它们必须在函数之后被销毁——否则它们永远不会被销毁(如果没有 classes),或者会跨越 class 边界。重要的一点是 class-scope(或任何其他范围)并不意味着 fixture 仅应用于该范围(例如在 class 内),而是应用于任何测试函数,并且范围只是大约在它被摧毁的时候。

对于不存在于 class 中的函数,class 范围的固定装置的行为与函数范围的固定装置一样,但它们在函数范围的固定装置之前被调用并在基于功能的装置:

conftest.py

@pytest.fixture(scope="function", autouse=True)
def fct_fixt():
    print("function fixture start")
    yield
    print("function fixture end")


@pytest.fixture(scope="class", autouse=True)
def class_fixt():
    print("class fixture start")
    yield
    print("class fixture end")

test_fixt.py

def test_1():
    print("test_1 outside class")

class TestClass:
    def test_1(self):
        print("test_1 inside class")

    def test_class_2(self):
        print("test_2 inside class")
$ python -m pytest -s test_fixt.py

...
class fixture start
  function fixture start
    test_1 outside class
  function fixture end
class fixture end
class fixture start
  function fixture start
    test_1 inside class
  function fixture end
  function fixture start
    test_2 inside class
  function fixture end
class fixture end

(为清楚起见添加了缩进)