2011-08-04 30 views
4

前幾天我剛開始使用SQLAlchemy,現在我遇到了一個問題,我希望任何人都可以在釋放所有頭髮之前揭示一些問題。SQLAlchemy session unit vtest in unittest

當我運行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() 

UPDATE

這裏是工作的代碼片段。

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

回答

6

此行爲最可能的原因是數據在測試之間沒有正確清理。這就解釋了爲什麼當你只運行一個測試時,它就可以工作。
setUp每次測試前都會調用,並且在tearDown之後。
取決於你想實現什麼,你有兩個選擇:

  1. 所有測試數據創建只有一次。
    在這種情況下,如果您有Python-2.7 +或Python-3.2 +,則可以使用tearDownClass方法。在你的情況下,你可以使用布爾類變量來處理它,以防止setUp中的代碼多次運行。
  2. 重新創建每次測試
    在這種情況下,你需要確保的是,在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 
+0

Hi Van!感謝您的意見。在tearDown中,我已經把Base.metadata.drop_all() - 不會刪除有問題的表,並因此在每次測試後刪除數據? –

+0

你說得對。但由於某些原因,數據實際上並未被第二次插入。如果您爲引擎設置了'echo = True'並查看生成的SQL語句,則會看到數據行沒有被第二次插入。在這一點上,我不知道爲什麼。不過,您可以實現選項1並僅創建一次數據(如果測試不刪除它),這將解決問題。 – van

+0

刪除setUp中的提交併在tearDown中關閉會話之前添加回滾之後,它將起作用。嗯... –

0

我有這個作爲我tearDown方法和它的工作罰款我的測試:

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

感謝您的回答。如果我需要,我會記住clear_mappers。如果您好奇,我剛剛用一段代碼更新了我的問題。 –