0
0
PyTesttesting~15 mins

Database rollback fixtures in PyTest - Build an Automation Script

Choose your learning style9 modes available
Test database rollback using pytest fixture
Preconditions (3)
Step 1: Create a pytest fixture that starts a database transaction before each test
Step 2: Insert a new user record with name 'Test User' inside the test
Step 3: Query the database inside the test to verify the user was inserted
Step 4: End the test without committing the transaction
Step 5: Verify after the test that the inserted user does not exist in the database
✅ Expected Result: The inserted user record is visible inside the test but is removed after the test due to rollback
Automation Requirements - pytest
Assertions Needed:
Assert the user record exists inside the test after insertion
Assert the user record does not exist after the test completes
Best Practices:
Use a pytest fixture with scope='function' to manage transactions
Use explicit transaction begin and rollback calls
Avoid committing inside tests to ensure rollback
Use SQLAlchemy session or equivalent for database operations
Keep tests isolated and independent
Automated Solution
PyTest
import pytest
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker

# Setup database engine and sessionmaker
engine = create_engine('sqlite:///test.db', echo=False, future=True)
Session = sessionmaker(bind=engine, future=True)

@pytest.fixture(scope='function')
def db_session():
    session = Session()
    connection = session.connection()
    trans = connection.begin()  # start transaction
    try:
        yield session
    finally:
        trans.rollback()  # rollback after test
        session.close()


def test_insert_user(db_session):
    # Insert user
    db_session.execute(text("INSERT INTO users (id, name) VALUES (1, 'Test User')"))
    db_session.commit()  # commit inside session to persist in transaction

    # Query to verify insertion
    result = db_session.execute(text("SELECT name FROM users WHERE id=1")).fetchone()
    assert result is not None
    assert result[0] == 'Test User'


def test_user_not_persisted():
    # New session to check if user exists after rollback
    with Session() as session:
        result = session.execute(text("SELECT name FROM users WHERE id=1")).fetchone()
        assert result is None

The db_session fixture creates a new database session and starts a transaction before each test. It yields the session to the test function. After the test finishes, the fixture rolls back the transaction to undo any changes made during the test, ensuring the database stays clean.

In test_insert_user, we insert a user and commit inside the transaction so the data is visible within the test. We then query to confirm the user exists. Because the fixture rolls back after the test, the inserted user is not saved permanently.

The test_user_not_persisted test opens a new session and verifies that the user inserted in the previous test does not exist, confirming the rollback worked.

This approach keeps tests isolated and prevents side effects between tests.

Common Mistakes - 4 Pitfalls
{'mistake': 'Committing the transaction inside the test and not rolling back in the fixture', 'why_bad': 'This causes test data to persist in the database, breaking test isolation and causing flaky tests.', 'correct_approach': "Always rollback the transaction in the fixture's teardown to undo changes regardless of commits inside tests."}
Using a fixture with module or session scope for database transactions
Not using explicit transactions and relying on autocommit mode
{'mistake': 'Not closing the session after the test', 'why_bad': 'Leads to resource leaks and connection exhaustion.', 'correct_approach': "Close the session in the fixture's finally block."}
Bonus Challenge

Now add data-driven testing with 3 different user names to insert and verify rollback for each

Show Hint