Hi, First of all, thanks for all the hard work and quality that goes into Pyramid to Chris and his team!
At my job, we're using Pyramid to do a massive migration from both legacy ASP code and also Pylons code. I'm doing some unit and functional tests on my views and I have a couple of questions. First, for all of its benefits, I've sometimes found the Zope Transaction Manager to be a big pain at times, but a lot of code has been assuming it's use, so for better or worse, we're stuck with it. The first issue relates to using WebTest for full-framework functional testing: how does one get session objects attached to the request object when WebTest (TestApp) uses WebOb's request object and not Pyramid's request object and all of its goodies? Will I have to subclass WebTest objects and add Pyramid features? Is there an easier way? For now, I went with attaching a session dict to a DummyRequest() testing object and just calling view code ala a simple unit test...but...for certain views that brings me to problem number two: SQLAlchemy appears to complain about a scoped session already existing, and throws a strange error on the tearDown() of a bit of data that happens within the view. It seems to be trying to UPDATE (during the tearDown()) the table that gets INSERTED to within the view code being called by the unit test. The unit test of course sets upand tears down its own tables and data. I have no problem with such things when views under unit test are doing simple queries, but once they start to INSERT and such, SQLAlchemy refuses to behave. I've tried countless options, and spent literally hours working around SQLAlchemy and the Zope TM, but in this particular case, I'm stuck and could use some other people's insight. Here's some code to show what's going on, followed by errors reported: from tests/__init__.py: def _initTestingDB(): from sqlalchemy import create_engine from pyramid_helios.models.meta import ( DBSession, Base ) engine = create_engine('sqlite://') Base.metadata.create_all(engine) DBSession.configure(bind=engine) return engine, DBSession class UnitTest(unittest.TestCase): ''' Base class for tests ''' def setUp(self): template_dir = os.path.join(os.path.dirname( os.path.dirname(os.path.abspath(__file__))), 'templates') self.config = testing.setUp(settings={'production' : 'false', 'test_mode' : 'true', 'letters.pdf.formsdir' :'$(here)s/forms', 'notepad_provider' : 'null', 'mako.directories' : template_dir, 'email_to_test' : 'm...@mycompany.com', 'email_to' : 'webt...@mycompany.com', 'email.paperless.name' : 'alert thing' 'email.paperless.address' : 'testale...@mycompany.com' }) self.config.include('pyramid_helios.lib.email.testing') self.engine, self.session = _initTestingDB() from pyramid_helios import models self.env = models self.dbfixture = SQLAlchemyFixture( env=models, engine=self.engine, style=NamedDataStyle() ) def tearDown(self): ''' Clears away any existing objects between tests. ''' self.session.remove() testing.tearDown() here's the view I'm testing: @view_config(route_name='payment_cc_setup') def payment_cc_setup(request): ''' This is the popup response to registering a card. The contents will be displayed within a 'fancybox' JavaScript popup''' #### needed variables from the request and session: session = request.session refn = session['refn'] log.debug('refn from session is %s' % refn) if request.params['Result'] == 'ERROR': message = request.params['MESSAGE'] return Response('''We detect the presence of an error. The message sent was: <br><br> %s''' % message) else: rrno = request.params['RRNO'] description = request.params['CARD_TYPE'] + ' ending in ' + request.params['PAYMENT_ACCOUNT'] cc_exp_date = request.params['CARD_EXPIRE'] transaction.get() ses = DBSession() ses.begin(subtransactions=True) ses.add(SavedAccts(refnum='2234123412', rrno=rrno, description=description, cc_exp_date=cc_exp_date, full_name='', transactiontime=datetime.datetime.now())) ses.flush() return Response('''You have successfully set up a new account into your stored accounts database. You may now use the dropdown list to make a payment against your loan(s).''') NOTE: I've tried different permutations of INSERT in various SQLAlchemy styles: non-scoped sessions, just using the connection itself and doing a raw query using 'text' in SQLAlchemy (this works, but other things don't work then in the unittest.... and here's the unit test. As you can see I've tried various ways of setting up the verification that the view's insert went ok, but when they 'work', the tearDown throws an error (see below): def test_payment_cc_setup_approved(self): req = DummyRequest() req.session = {'refn' : '2234123412'} req.method = 'POST' req.params = {'Result' : 'Approved', 'MESSAGE' : 'Good job!', 'RRNO' : '100198989797', 'CARD_TYPE' : 'VISA', 'PAYMENT_ACCOUNT' : 'xxxxxxxxxxxx4444', 'CARD_EXPIRE' : '0317'} resp = payment_cc_setup(req) self.assertIn('You may now', resp.body) #self.session.begin(subtransactions=True) #q = self.session.query(SavedAccts).filter(SavedAccts.refnum=='2234123412').all() q = ''' SELECT * from borr_saved_accts WHERE refnum=='2234123412' ''' rows = self.session.connection().execute(q).fetchall() print 'rows.description is %s' % rows[0].description assert False now, if I run this with 'nosetests', here's the error: E/home/ajohnson/venv/migration/pyramid_helios/pyramid_helios/tests/__init__.py:46: SAWarning: At least one scoped session is already present. configure() can not affect sessions that have already been created. DBSession.configure(bind=engine) . ====================================================================== ERROR: test_payment_cc_setup_approved (pyramid_helios.tests.views.test_payments.TestPaymentsUnit) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/ajohnson/venv/migration/pyramid_helios/pyramid_helios/tests/views/test_payments.py", line 198, in tearDown self.data.teardown() File "/home/ajohnson/venv/lib/python2.7/site-packages/fixture-1.4-py2.7.egg/fixture/base.py", line 75, in teardown self.loader.unload() File "/home/ajohnson/venv/lib/python2.7/site-packages/fixture-1.4-py2.7.egg/fixture/loadable/loadable.py", line 299, in unload self.wrap_in_transaction(unloader, unloading=True) File "/home/ajohnson/venv/lib/python2.7/site-packages/fixture-1.4-py2.7.egg/fixture/loadable/loadable.py", line 315, in wrap_in_transaction self.commit() File "/home/ajohnson/venv/lib/python2.7/site-packages/fixture-1.4-py2.7.egg/fixture/loadable/sqlalchemy_loadable.py", line 149, in commit self.session.flush() File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/session.py", line 1587, in flush self._flush(objects) File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/session.py", line 1658, in _flush flush_context.execute() File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/unitofwork.py", line 331, in execute rec.execute(self) File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/unitofwork.py", line 475, in execute uow File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/persistence.py", line 59, in save_obj mapper, table, update) File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/persistence.py", line 485, in _emit_update_statements execute(statement, params) File "build/bdist.linux-x86_64/egg/sqlalchemy/engine/base.py", line 1450, in execute params) File "build/bdist.linux-x86_64/egg/sqlalchemy/engine/base.py", line 1583, in _execute_clauseelement compiled_sql, distilled_params File "build/bdist.linux-x86_64/egg/sqlalchemy/engine/base.py", line 1697, in _execute_context context) File "build/bdist.linux-x86_64/egg/sqlalchemy/engine/base.py", line 1690, in _execute_context context) File "build/bdist.linux-x86_64/egg/sqlalchemy/engine/default.py", line 331, in do_execute cursor.execute(statement, parameters) IntegrityError: (IntegrityError) borr_saved_accts.refnum may not be NULL u'UPDATE borr_saved_accts SET refnum=? WHERE borr_saved_accts.rrno = ?' (None, u'100198989797') -------------------- >> begin captured stdout << --------------------- rows.description is VISA ending in xxxxxxxxxxxx4444 --------------------- >> end captured stdout << ---------------------- -------------------- >> begin captured logging << -------------------- pyramid_helios.views.payments: DEBUG: refn from session is 2234123412 txn.140291144705792: DEBUG: new transaction --------------------- >> end captured logging << --------------------- ---------------------------------------------------------------------- Ran 2 tests in 0.419s FAILED (errors=1) The IntegrityError thing is what I just don't get; it happens on tearDown, andI don't know why. more broadly, how do I allow unitTest and view code DBSession/Transaction to play nice with each other when the view code needs to do destructive (and creative) ops to the data? Any help would be most appreciated! Best, AKJ -- You received this message because you are subscribed to the Google Groups "pylons-discuss" group. To unsubscribe from this group and stop receiving emails from it, send an email to pylons-discuss+unsubscr...@googlegroups.com. To post to this group, send email to pylons-discuss@googlegroups.com. Visit this group at http://groups.google.com/group/pylons-discuss?hl=en. For more options, visit https://groups.google.com/groups/opt_out.