Сеанс SQLAlchemy в юнит-тесте - PullRequest
       6

Сеанс SQLAlchemy в юнит-тесте

4 голосов
/ 04 августа 2011

Я только начал использовать SQLAlchemy несколько дней назад, и сейчас я застрял с проблемой, которая, я надеюсь, кто-нибудь может пролить немного света, прежде чем я потерю все свои волосы.

Когда я запускаюunittest, см. фрагмент ниже, только первый тест в последовательности проходит.Тест testPhysicalPrint работает просто отлично, но testRecordingItem завершается ошибкой с исключением NoResultFound - Не найдено ни одной строки для одного () .Но если я удаляю testPhysicalPrint из класса test, то testRecordingItem работает.

Я предполагаю, что проблема связана с сеансом, но я не могу справиться с этим.

В случае, если кому-то интересно, установка выглядит следующим образом:

  • Python 3.1 (пакет Ubuntu 10.04)
  • SQLAlchemy 0.7.2 (easy_install: ed)
  • PostgreSQL8.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 ]

6 голосов
/ 05 августа 2011

Наиболее вероятной причиной такого поведения является тот факт, что данные не очищаются должным образом между тестами.Это объясняет, почему, когда вы запускаете только один тест, он работает.
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 голосов
/ 29 ноября 2011

У меня есть метод tearDown, и он отлично работает для моих тестов:

def tearDown (self):
    """Cleans up after each test case."""
    sqlalchemy.orm.clear_mappers()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...