Search code examples
python-3.xpeewee

When do you exit the context of a pytest fixture?


I created a fixture to initialize a database

@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)

My test uses this fixture to operate on a clean in-memory database:

@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

When this fixture yields what is actually triggering the finally? Is it when the test case ends and the scope that the fixture was passed to is exited?


Solution

  • I simplified your test_db fixture to the following:

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

    And your test function to:

    @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
    

    When I ran the test with pytest -sv (-s captures all prints, and -v makes output verbose), I got output similar to below:

    ============================= 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 ===========================
    

    Note that Finally executed was printed after the test finishes.

    So I would agree with your guess that the finally statement was executed after the fixture was destroyed.

    Further, I think at the end of a test scope, pytest does something similar to

    try:
        next(yielded_fixture)
    except StopIteration:
        pass
    

    in order to execute whatever teardown statement is written after the yield statement in the fixture function.