什么时候退出 pytest fixture 的上下文?

When do you exit the context of a pytest fixture?

我创建了一个固定装置来初始化数据库

@pytest.fixture
def test_db():
    """Setup Database"""
    _db = SqliteDatabase(":memory:")
    dbs = (Resource, Reservation, Token)
    with _db.bind_ctx(dbs):
        _db.create_tables(dbs)
        try:
            yield test_db
        finally:
            _db.drop_tables(dbs)

我的测试使用此夹具在干净的内存数据库上运行:

@pytest.mark.parametrize(
    "attrs, exception, std",
    [
        (
            {"name": "Test1", "count": 1, "resource_type": "test_flavor"},
            peewee.IntegrityError,
            "resource.max_reservation_time may not be NULL",
        ),
    ],
)
def test_bad_resoruce_create(attrs, exception, std, test_db):
    with pytest.raises(exception) as db_error:
        resource = Resource.create(**attrs)
    assert str(db_error.value) == std

当这个夹具 yields 实际触发 finally 的是什么?是在测试用例结束并且 fixture 传递到的范围退出时吗?

我将您的 test_db 夹具简化为以下内容:

@pytest.fixture
def test_db():
    """Setup Database"""
    print("\nInitialized resources")
    try:
        yield test_db
    finally:
        print("\nFinally executed")

而你的测试功能为:

@pytest.mark.parametrize(
    "attrs, exception, std",
    [ ( "attrs1", "ErrorObject", "std",) ]
)
def test_bad_resoruce_create(test_db, attrs, exception, std):
    print("Doing testings")
    assert 1 == 1

当我 运行 使用 pytest -sv 进行测试时(-s 捕获所有 prints,并且 -v 使输出冗长),我得到类似的输出到下面:

============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-4.2.1, py-1.8.0, pluggy-0.9.0 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: , inifile:
plugins: flask-0.14.0
collected 1 item                                                               

try.py::test_bad_resoruce_create[attrs1-ErrorObject-std] 
Initialized resources
Doing testings
PASSED
Finally executed


=========================== 1 passed in 0.10 seconds ===========================

请注意,Finally executed 是在测试完成后打印的。

所以我同意你的猜测,即 finally 语句是在 fixture 被销毁后执行的。

此外,我认为在测试范围的末尾,pytest 会做一些类似于

的事情
try:
    next(yielded_fixture)
except StopIteration:
    pass

为了执行 fixture 函数中 yield 语句之后编写的任何拆卸语句。