SQLAlchemy Session Sessions in Unittest

I just started using SQLAlchemy a few days ago, and now I have a problem that I hope someone can shed some light before I lose all my hair.

When I run unittest, see the snippet below, only the first test in the sequence is executed. The testPhysicalPrint test works very well, but testRecordingItem does not work with the NoResultFound exception. No rows were found for one (). But if I remove testPhysicalPrint from the test class, then testRecordingItem will work.

I suppose the problem has something to do with the session, but I cannot understand it.

In case someone wonders, the setup is as follows:

  • Python 3.1 (Ubuntu 10.04 package)
  • SQLAlchemy 0.7.2 (easy_install: ed)
  • PostgreSQL 8.4.8 (Ubuntu 10.04 package)
  • PsycoPG2 2.4.2 (easy_installed: ed)

Verification Example:

class TestSchema(unittest.TestCase): test_items = [ # Some parent class products PrintItem(key='p1', title='Possession', dimension='30x24'), PrintItem(key='p2', title='Andrzej Żuławski - a director', dimension='22x14'), DigitalItem(key='d1', title='Every Man His Own University', url='http://www.gutenberg.org/files/36955/36955-h/36955-h.htm'), DigitalItem(key='d2', title='City Ballads', url='http://www.gutenberg.org/files/36954/36954-h/36954-h.htm'), ] def testPrintItem(self): item = self.session.query(PrintItem).filter(PrintItem.key == 'p1').one() assert item.title == 'Possession', 'Title mismatch' def testDigitalItem(self): item2 = self.session.query(DigitalItem).filter(DigitalItem.key == 'd2').one() assert item2.title == 'City Ballads', 'Title mismatch' def setUp(self): Base.metadata.create_all() self.session = DBSession() self.session.add_all(self.test_items) self.session.commit() def tearDown(self): self.session.close() Base.metadata.drop_all() if __name__ == '__main__': unittest.main() 

UPDATE

Here is a snippet of working code.

 # -*- coding: utf-8 -*- import time import unittest from sqlalchemy import * from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import * Base = declarative_base() engine = create_engine('sqlite:///testdb', echo=False) DBSession = sessionmaker(bind=engine) class ItemMixin(object): """ Commons attributes for items, ie books, DVD:s... """ __tablename__ = 'testitems' __table_args__ = {'extend_existing':True} id = Column(Integer, autoincrement=True, primary_key=True) key = Column(Unicode(16), unique=True, nullable=False) title = Column(UnicodeText, default=None) item_type = Column(Unicode(20), default=None) __mapper_args__ = {'polymorphic_on': item_type} def __init__(self, key, title=None): self.key = key self.title = title class FooItem(Base, ItemMixin): foo = Column(UnicodeText, default=None) __mapper_args__ = {'polymorphic_identity':'foo'} def __init__(self, foo=None, **kwargs): ItemMixin.__init__(self, **kwargs) self.foo = foo class BarItem(Base, ItemMixin): bar = Column(UnicodeText, default=None) __mapper_args__ = {'polymorphic_identity':'bar'} def __init__(self, bar=None, **kwargs): ItemMixin.__init__(self, **kwargs) self.bar = bar # Tests class TestSchema(unittest.TestCase): # Class variables is_setup = False session = None metadata = None test_items = [ FooItem(key='f1', title='Possession', foo='Hello'), FooItem(key='f2', title='Andrzej Żuławsk', foo='World'), BarItem(key='b1', title='Wikipedia', bar='World'), BarItem(key='b2', title='City Ballads', bar='Hello'), ] def testFooItem(self): print ('Test Foo Item') item = self.__class__.session.query(FooItem).filter(FooItem.key == 'f1').first() assert item.title == 'Possession', 'Title mismatch' def testBarItem(self): print ('Test Bar Item') item = self.__class__.session.query(BarItem).filter(BarItem.key == 'b2').first() assert item.title == 'City Ballads', 'Title mismatch' def setUp(self): if not self.__class__.is_setup: self.__class__.session = DBSession() self.metadata = Base.metadata self.metadata.bind = engine self.metadata.drop_all() # Drop table self.metadata.create_all() # Create tables self.__class__.session.add_all(self.test_items) # Add data self.__class__.session.commit() # Commit self.__class__.is_setup = True def tearDown(self): if self.__class__.is_setup: self.__class__.session.close() # Just for Python >=2.7 or >=3.2 @classmethod def setUpClass(cls): pass #Just for Python >=2.7 or >=3.2 @classmethod def tearDownClass(cls): pass if __name__ == '__main__': unittest.main() 
+4
source share
2 answers

The most likely reason for this behavior is the fact that this data is incorrectly cleared between tests. This explains why when you run only one test, it works.
setUp is called before each test, and tearDown is called after.
Depending on what you would like to achieve, you have two options:

  • Create data only once for all tests.
    In this case, if you have Python-2.7 + or Python-3.2 +, you can use the tearDownClass method. In your case, you can process it with a variable of type boolean to prevent the code that you have in setUp being executed more than once.
  • re-create data before each test
    In this case, you need to make sure that in tearDown you delete all the data. This is something you are not doing right now, and I suspect that when you run the second test, calling one() fails not because it does not find the object, but because it finds more than two objects that match the criteria.

Check the output of this code to understand the call sequence:

 import unittest class TestSchema(unittest.TestCase): def testOne(self): print '==testOne' def testTwo(self): print '==testTwo' def setUp(self): print '>>setUp' def tearDown(self): print '<<tearDown' @classmethod def setUpClass(): print '>>setUpClass' @classmethod def tearDownClass(): print '<<tearDownClass' if __name__ == '__main__': unittest.main() 

Output:

 >>setUp ==testOne <<tearDown >>setUp ==testTwo <<tearDown 
+6
source

I have it like my tearDown method and it works fine for my tests:

 def tearDown (self): """Cleans up after each test case.""" sqlalchemy.orm.clear_mappers() 
0
source

All Articles