I've been struggling with this for a few days now, and I've tried a whole 
slew of different approaches, but I'm just missing something.

I started with the example 
at 
http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#joining-a-session-into-an-external-transaction-such-as-for-test-suites
 
and have been trying variations on it to suit my goals.

We have a webapp which, in simplest form, could be represented as this:

# in the db setup file

dsn = 'mysql://...t'
engine = create_engine(dsn)
session_factory = sessionmaker(bind=engine)
scoped = scoped_session(session_factory)

# the app

class Webapp(object):

    def dispatch(self, name):
        view = getattr(self, name)
        session = scoped()
        try:
            rsp = view()
            session.commit()
            return rsp
        except:
            session.rollback()
            return 'error'
        finally:
            session.close()
            pass

# the views files

    def add_user(self, session):
        user = User(username='test')
        session = scoped()
        session.add(user)
        session.flush()
        return 'user %s' % user.id

The app uses commit, rollback, and close on a scoped session.

I'd like to know how to adjust the example at the above URL so the test is 
able to "enclose" the webapp, so the calls to sessionmaker, and all 
operation that occur within the webapp, are within the scope of the test 
transaction, so everything that happens in the app can be rolled back at 
the end of the test. The example at the url acquires a connection, and 
binds the session to the connection, and I'm not sure how to force the 
sessionmaker to work with that. Could 'scopefunc' be used to somehow force 
the returned session to be within the context of the transaction?

There is also fixture loading code, which starts by acquiring a session 
from the scoped_session, adding fixtures, then committing. A simple example 
could be this function:

def load_fixture(username):
    session = scoped()
    session.add(User(username=username))
    session.commit()

I'd like to use this in unittests, and have it rolled back along with the 
transaction.

I attached a full (nonworking) example, can you tell me what I am doing 
wrong? Is this even possible with a scope session, or is this sort of 
testing limited to to sessions bound to connections? Would I need to 
rewrite the session acquisition method to return a globally stored 
connection-bound session before defaulting to the scoped (engine-bound) 
session?

An example I saw 
at 
https://web.archive.org/web/20140419235219/http://sontek.net/blog/detail/writing-tests-for-pyramid-and-sqlalchemy
 
seems to indicate that person overwrote the app session from within the 
unittest, to ensure the app used that session. Is that the approach I would 
need to take?

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
from sqlalchemy import *
from sqlalchemy import event
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
import unittest


dsn = 'mysql://...'
engine = create_engine(dsn)
session_factory = sessionmaker(bind=engine)
scoped = scoped_session(session_factory)
Base = declarative_base()

class User(Base):
    __tablename__ = 'test_users'
    username = Column(String(50), primary_key=True)

    def __repr__(self):
        return "'%s'" % self.username


class Webapp(object):

    def dispatch(self, name):
        # in the real app, views are spread out over the fs
        # and are looked up via a cached url configuration,
        # they are not attributes of the base app
        view = getattr(self, name)
        session = scoped()
        try:
            rsp = view()
            session.commit()
            return rsp
        except:
            session.rollback()
            return 'error'
        finally:
            session.close()
            pass

    def add_user(self):
        session = scoped()
        user = User(username='app')
        session.add(user)
        session.flush()
        return 'user %s' % user.username

    def view_user(self):
        session = scoped()
        user = session.query(User).get('setup')
        return 'user %s' % user.username

    def delete_user(self):
        session = scoped()
        user = session.query(User).get('setup')
        session.delete(user)
        session.flush()
        return 'deleted'

def load_fixture(username):
    session = scoped()
    session.add(User(username=username))
    session.commit()

def setUpModule():
    print 'setup module'
    Base.metadata.bind = engine
    Base.metadata.drop_all()
    Base.metadata.create_all()

def tearDownModule():
    print 'teardown module'

class SomeTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print 'setup class'
        cls.app = Webapp()
        cls.session = scoped()
        cls.trans1 = cls.session.begin_nested()
        load_fixture('setupclass')
        print 'post-setupclass users:', cls.session.query(User).all()

    @classmethod
    def tearDownClass(cls):
        print 'pre-teardown class users:', cls.session.query(User).all()
        cls.trans1.rollback()
        cls.session.close()
        print 'post-teardown class users:', cls.session.query(User).all()

    def setUp(self):
        print '\npre-setup users:', self.session.query(User).all()
        self.trans2 = self.session.begin_nested()
        load_fixture('setup')
        print 'post-setup users:', self.session.query(User).all()
        
    def tearDown(self):
        print 'pre-teardown users:', self.session.query(User).all()
        self.trans2.rollback()
        print 'post-teardown users:', self.session.query(User).all()

    #def test_view(self):
    #    rsp = self.app.dispatch('view_user')
    #    print rsp

    def test_add(self):
        rsp = self.app.dispatch('add_user')
        print rsp

    #def test_delete(self):
    #    rsp = self.app.dispatch('delete_user')
    #    print rsp



if __name__ == "__main__":
    unittest.main()

Reply via email to