SQLAlchemy会话在unittest中发挥作用

时间:2011-08-04 12:26:12

标签: sqlalchemy

我几天前刚刚开始使用SQLAlchemy而且现在我遇到了一个问题,我希望任何人都可以在我松开头发之前先解决这个问题。

当我运行unittest时,请参阅下面的代码段,只有序列中的第一个测试正在通过。测试testPhysicalPrint工作正常,但testRecordingItem失败,NoResultFound异常 - 没有找到一行()的行。但是如果我从测试类中删除testPhysicalPrint,那么testRecordingItem就可以工作。

我认为问题与会话有关,但我无法抓住它。

如果有人想知道,设置如下:

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

例子测试:

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()

更新

以下是工作代码段。

# -*- 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()

2 个答案:

答案 0 :(得分:6)

此行为的最可能原因是在测试之间未正确清理数据。这就解释了为什么当你只运行一次测试时,它会起作用 每次测试前都会调用setUp,之后会tearDown - 根据您希望实现的目标,您有两种选择:

  1. 仅为所有测试创建一次数据。
    在这种情况下,如果您使用的是Python-2.7 +或Python-3.2 +,则可以使用tearDownClass方法。在您的情况下,您可以使用布尔类变量来处理它,以防止setUp中运行的代码多运行一次。
  2. 在每次测试前重新创建数据
    在这种情况下,您需要确保在tearDown中删除所有数据。这就是你现在没做的事情,我怀疑当第二次测试运行时,对one()的调用失败不是因为它找不到对象,而是因为它找到了更多符合条件的两个对象。

  3. 检查此代码的输出以了解调用顺序:

    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()
    

    输出:

    >>setUp
    ==testOne
    <<tearDown
    >>setUp
    ==testTwo
    <<tearDown
    

答案 1 :(得分:0)

我将此作为我的tearDown方法,它确实适用于我的测试:

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