0

i write a pytest fixture to rollback data after each test function has run, which seems to make no sense. The fixture is below:

@pytest.fixture(scope='function')
def session(db, request):
    """Creates a new database session for a test."""
    connection = db.engine.connect()
    transaction = connection.begin()
    options = dict(bind=connection, binds={})
    session = db.create_scoped_session(options=options)
    db.session = session

    def teardown():
        """rollback data after every function has run"""
        transaction.rollback()
        session.commit()
        connection.close()
        session.remove()
    request.addfinalizer(teardown)
    return session

the session can be use as the argument of test function, but the transaction.rollback() seems to not work. how can i modify it to make it work ?

update:

for the tail, the origin fixture is

`@pytest.fixture(scope='function')
def session(db, request):
    """Creates a new database session for a test."""
    connection = db.engine.connect()
    transaction = connection.begin()
    options = dict(bind=connection, binds={})
    session = db.create_scoped_session(options=options)
    db.session = session

    def teardown():
        """rollback data after every function has run"""
        transaction.rollback()
        session.commit()
        connection.close()
        session.remove()

    def clear_all_data():
        meta = db.metadata
        for tl in reversed(meta.sorted_tables):
            # print('clear table %s' % tl)
            db.session.execute(tl.delete())

        db.session.commit()

    request.addfinalizer(teardown)
    return session`

and test function is

`def test_check_name(client, session, db):
    # name can not be blank
    cur_url = BLUEPREFIX + '/checkname'
    rv = client.post(cur_url).data.decode('utf-8')
    assert get_json_err_no(rv, '15')

    # name can not exist already
    customer = Customer(name='wt', pinyin='wt')
    session.add(customer)
    session.commit()
    rv = client.post(cur_url, data=dict(
        name='wt')).data.decode('utf-8')
    assert get_json_err_no(rv, '14')

    rv = client.post(cur_url, data=dict(
        name='wtf')).data.decode('utf-8')
    assert get_json_err_no(rv, '17')
def test_del_customer(client, session, db):
    cur_url = BLUEPREFIX + '/del'

    # check invalid post
    rv = client.post(cur_url).data.decode('utf-8')
    assert get_json_err_no(rv, '18')

    rv = client.post(cur_url, data=dict(
        customer_id='1a')).data.decode('utf-8')
    assert get_json_err_no(rv, '8')

    print(Customer.query.count())
    rv = client.post(cur_url, data=dict(
        customer_id='1')).data.decode('utf-8')
    assert get_json_err_no(rv, '7')

    customer = Customer(name='wt', pinyin='wt')
    session.add(customer)
    session.commit()
    assert customer.id == 1

    rv = client.post(cur_url, data=dict(
        customer_id=str(customer.id))).data.decode('utf-8')
    assert get_json_err_no(rv, '0')`

i run py.test -s -v -k "not add" and the result is

`============================================= test session starts =============================================

...

test_customer.py::test_check_name --------------------------------------------------------------------------------
DEBUG in customer [/home/dyh/svn/hs/hs/views/customer.py:21]:
customer of same name exists !
--------------------------------------------------------------------------------
PASSED
test_customer.py::test_del_customer 1
FAILED
test_helloworld.py::test_answer PASSED

================================================== FAILURES ===================================================
______________________________________________ test_del_customer ______________________________________________

client = <FlaskClient <Flask 'hs'>>, session = <sqlalchemy.orm.scoping.scoped_session object at 0x7f06da034898>
db = <SQLAlchemy engine='sqlite:////tmp/tmp90nyrdqr'>

    def test_del_customer(client, session, db):
        cur_url = BLUEPREFIX + '/del'

        # check invalid post
        rv = client.post(cur_url).data.decode('utf-8')
        assert get_json_err_no(rv, '18')

        rv = client.post(cur_url, data=dict(
            customer_id='1a')).data.decode('utf-8')
        assert get_json_err_no(rv, '8')

        print(Customer.query.count())
        rv = client.post(cur_url, data=dict(
            customer_id='1')).data.decode('utf-8')
>       assert get_json_err_no(rv, '7')
E       assert get_json_err_no('{\n  "err_msg": "\\u64cd\\u4f5c\\u6210\\u529f!",\n  "err_no": "0"\n}', '7')

test_customer.py:90: AssertionError
====================================== 1 tests deselected by '-knot add' ======================================
============================== 1 failed, 2 passed, 1 deselected in 0.06 seconds ===============================`
jjwt
  • 5
  • 1
  • 6
  • Probably why your rollback isn't working is because `teardown` is an inner function and never gets called. – larsbutler Mar 10 '16 at 09:54
  • @larsbutler sorry, my mistake, it is part of the whole code, the original is added. i edit the the code to the original one, and it still does not work. – jjwt Mar 10 '16 at 10:08

1 Answers1

1

Instead of using addfinalizer, check this manual, and use yield_fixture py.test feature. Lightweight example:

import pytest

@pytest.yield_fixture
def session(db):
    connection = db.engine.connect()
    # ...
    session = db.create_scoped_session()
    yield session
    session.remove()
    # ...
    connection.close()

Update: 2021-12-08

Important:

Since pytest-3.0, fixtures using the normal fixture decorator can use a yield statement to provide fixture values and execute teardown code, exactly like yield_fixture in previous versions.

Marking functions as yield_fixture is still supported, but deprecated and should not be used in new code.

Sergei Voronezhskii
  • 2,184
  • 19
  • 32