单元测试中的 SQLAlchemy session 投票

标签 sqlalchemy

几天前我刚刚开始使用 SQLAlchemy,现在我遇到了一个问题,我希望任何人都可以在我失去所有头发之前弄清楚。

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

我认为问题与 session 有关,但我无法真正掌握它。

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

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

    最佳答案

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

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


  • 检查此代码的输出以了解调用序列:
    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
    

    关于单元测试中的 SQLAlchemy session 投票,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6941368/

    相关文章:

    python - SQLAlchemy 和标量值

    python - SQLAlchemy 和消息消费者 - session 应该放在哪里?

    python - 将 SQLAlchemy 记录在单独的文件中

    python - 如何使用 python 重新启动 mysql 中保存的调度程序

    python - 如何使用 sqlalchemy 和 postgres 使用嵌套 jsonb 进行过滤

    python - Sqlalchemy 一对多关系加入?

    python - 无法捕获 SQLAlchemy IntegrityError

    sqlalchemy - 如何按列分数从表中选择最小值和最大值?

    python - 为什么在获取数据时要进行 NAN 值?

    python - 如何使用 SQLAlchemy 连接 Windows 10 中 Docker 上的 Postgres 数据库?