On Fri, Oct 11, 2013 at 5:53 PM, Michael Bayer <mike...@zzzcomputing.com>wrote:

>
> On Oct 11, 2013, at 7:14 PM, Ken Lareau <klar...@tagged.com> wrote:
>
> In the process of trying to find an efficient way to manage a test database
> for a large set of tests for a database library I'm writing that uses
> SQLAlchemy,
> I came across this page:
>
>
> http://alextechrants.blogspot.fi/2013/08/unit-testing-sqlalchemy-apps.html
>
> This is definitely what I want to do, with one catch: I already have a
> session
> management system in place for the library that seems to conflict with the
> sample code given on the webpage, and I'm not having luck reconciling it.
>
>
> I find the approach on that page a little awkward - it's using new globals
> for no good reason and also the begin_nested() seems strange.   The test
> itself then has a "self.session", so the test itself is using a test-bound
> session, the choice of globals for "connection" and "engine" seems even
> more weird.
>

Yeah, after looking at it a bit, it also seemed awkward to me, but the
reason
I was looking is that it takes a good full 7 seconds to recreate an empty
database
and doing that per test or even per module might get a bit painful
(eventually
I suspect this library will have quite a few test modules to match all the
tables
and uses of the library), but maybe I'll just pursue that for now (I mostly
had
that working, at least).


> The way this works is:
>
> 1. test fixture gets at an engine, from configurational system, locally,
> whereever.
>
> 2. test fixture gets a connection, holds onto it locally.
>
> 3. test fixture gets a transaction from connection - this is a top level
> transaction, using connection.begin()
>
> 4. test fixture then does whatever the test needs to get at a session.  if
> the code being tested relies upon a global registry, it injects the
> connection.  below is using a traditional scoped session:
>
>     def setUp(self):
>         self.conn = engine.connect()
>         self.trans = self.conn.begin()
>
>         from application.model import the_scoped_session
>         self.session = the_scoped_session(bind=self.conn)
>
> now above, the test fixture has a hold on "self.session".  but - this is
> the *same* session that's in the registry (the registry here being
> "application.model.the_scoped_session").   if some other code somewhere
> calls upon the_scoped_session(), they get the *same* session.   it's a
> registry, that's the point of it.
>
> if you have some other kind of registration thing in place, you'd need to
> figure out how to load it up with a new Session bound to that local
> connection.
>
>
> 5. test fixture releases the session:
>
>     def tearDown(self):
>         the_scoped_session.remove()
>         self.trans.rollback()
>         self.conn.close()
>
> so note, we don't have to bother doing anything special with the Session
> at teardown time, we just dump it.   we roll back the transaction that
> we've created externally to it.
>
> an example of running through this is in the docs at:
>
>
> http://docs.sqlalchemy.org/en/rel_0_8/orm/session.html#joining-a-session-into-an-external-transaction
>
>
> and I am able to use Session to manage all my access to the ORM.
> My attempt to modify the code on the webpage currently looks like this:
>
> import unittest2 as unittest
>
> from tagopsdb.database import init_database, init_session
> from tagopsdb.database.meta import Session
>
>
> def setup_module():
>     global transaction, connection, engine
>
>     # Connect to the database and create the schema within a transaction
>     engine = init_session('dbtest', 'dbtestpasswd', hostname='localhost',
>                           db_name='TagOpsDB_Testing')
>     init_database()
>
>     # If you want to insert fixtures to the DB, do it here
>
>
> def teardown_module():
>     # Roll back the top level transaction and disconnect from the database
>     Session.rollback()
>     Session.close()
>     engine.dispose()
>
>
> I tend to organize things such that the scope of this transaction is *per
> test*, not per module as you're doing.  the engine, that can be per module,
> or preferably per-application.   But i'd be linking the lifespan of the
> Session to that of the transaction (which again begin_nested() should be a
> begin()).
>

My only concern with that is the enormous time to recreate/reset the
database for each test as mentioned above, but once again, I might
not be fully understanding if this is actually a concern or not.


I modified my test classes to subclass DatabaseTest, but an attempt
to run the tests results in:

UnboundExecutionError: Could not locate a bind configured on mapper
Mapper|Environments|environments or this Session


make sure the Session is created with an explicit bind to the connection.
>

I have this at the end of my init_session():

Session.configure(bind=engine)

but I'm assuming that's not enough?


-- 
- Ken Lareau

-- 
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 http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to