如何为每个单元测试正确设置单个 SQLAlchemy 会话?
How do I properly set up a single SQLAlchemy session for each unit test?
当使用 WebTest 测试我的 Pyramid 应用程序时,我无法在我的测试中 create/use 一个单独的 Session 而没有收到关于范围会话已经存在的警告。
这里是Pyramid应用的main()函数,这里是配置数据库的地方。
# __init__.py of Pyramid application
from pyramid_sqlalchemy import init_sqlalchemy
from sqlalchemy import create_engine
def main(global_config, **settings):
...
db_url = 'some-url'
engine = create_engine(db_url)
init_sqlalchemy(engine) # Warning thrown here.
这是测试代码。
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import init_sqlalchemy, Session
from sqlalchemy import create_engine
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Create session for tests.
db_url = 'same-url-as-above'
engine = create_engine(db_url)
init_sqlalchemy(engine)
# Note: I've tried both using pyramid_sqlalchemy approach here and
# creating a "plain, old" SQLAlchemy session here using sessionmaker.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user@email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
...
def tearDown(self):
self.base_tear_down()
def test_1(self):
# This is a typical workflow on my tests.
response = self.test_app.patch_json('/users/{0}'.format(self.user.id), {'email': 'new.email@email.com')
self.assertEqual(response.status_code, 200)
user = Session.query(User).filter_by(id=self.user.id).first()
self.assertEqual(user.email, 'new.email@email.com')
...
def test_8(self):
...
运行 测试给了我 8 个通过,7 个警告,其中除了第一个之外的每个测试都给出以下警告:
From Pyramid application: __init__.py -> main -> init_sqlalchemy(engine)
:
sqlalchemy.exc.SAWarning: At least one scoped session is already present. configure() can not affect sessions that have already been created.
如果这有用,我相信我在这里看到了同样的问题,只是我使用的是 pyramid_sqlalchemy 而不是创建我自己的 DBSession。
回答我自己的问题:我不确定这是否是最好的方法,但对我有用。
我没有尝试在我的测试中创建一个单独的会话,而是使用在应用程序中配置的 pyramid_sqlalchemy 会话工厂。据我所知,在测试和应用程序代码 return 中调用 Session
相同注册 scoped_session.
我为测试创建单独会话的初衷是确认记录已写入数据库,而不仅仅是在活动的 SQLAlchemy 会话中更新。通过这种新方法,我通过在测试事务和应用程序事务之间转换的测试点发出 Session.expire_all()
设法避免了这些 "caching" 问题。
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import Session
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Don't set up an additional session in the tests. Instead import
# and use pyramid_sqlalchemy.Session, which is set up in the application.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user@email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
Session.expire_all() # "Reset" your session.
def tearDown(self):
self.base_tear_down()
当使用 WebTest 测试我的 Pyramid 应用程序时,我无法在我的测试中 create/use 一个单独的 Session 而没有收到关于范围会话已经存在的警告。
这里是Pyramid应用的main()函数,这里是配置数据库的地方。
# __init__.py of Pyramid application
from pyramid_sqlalchemy import init_sqlalchemy
from sqlalchemy import create_engine
def main(global_config, **settings):
...
db_url = 'some-url'
engine = create_engine(db_url)
init_sqlalchemy(engine) # Warning thrown here.
这是测试代码。
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import init_sqlalchemy, Session
from sqlalchemy import create_engine
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Create session for tests.
db_url = 'same-url-as-above'
engine = create_engine(db_url)
init_sqlalchemy(engine)
# Note: I've tried both using pyramid_sqlalchemy approach here and
# creating a "plain, old" SQLAlchemy session here using sessionmaker.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user@email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
...
def tearDown(self):
self.base_tear_down()
def test_1(self):
# This is a typical workflow on my tests.
response = self.test_app.patch_json('/users/{0}'.format(self.user.id), {'email': 'new.email@email.com')
self.assertEqual(response.status_code, 200)
user = Session.query(User).filter_by(id=self.user.id).first()
self.assertEqual(user.email, 'new.email@email.com')
...
def test_8(self):
...
运行 测试给了我 8 个通过,7 个警告,其中除了第一个之外的每个测试都给出以下警告:
From Pyramid application:
__init__.py -> main -> init_sqlalchemy(engine)
: sqlalchemy.exc.SAWarning: At least one scoped session is already present. configure() can not affect sessions that have already been created.
如果这有用,我相信我在这里看到了同样的问题,只是我使用的是 pyramid_sqlalchemy 而不是创建我自己的 DBSession。
回答我自己的问题:我不确定这是否是最好的方法,但对我有用。
我没有尝试在我的测试中创建一个单独的会话,而是使用在应用程序中配置的 pyramid_sqlalchemy 会话工厂。据我所知,在测试和应用程序代码 return 中调用 Session
相同注册 scoped_session.
我为测试创建单独会话的初衷是确认记录已写入数据库,而不仅仅是在活动的 SQLAlchemy 会话中更新。通过这种新方法,我通过在测试事务和应用程序事务之间转换的测试点发出 Session.expire_all()
设法避免了这些 "caching" 问题。
# test.py (Functional tests)
import transaction
from unittest import TestCase
from pyramid.paster import get_appsettings
from pyramid_sqlalchemy import Session
from webtest import TestApp
from app import main
from app.models.users import User
class BaseTestCase(TestCase):
def base_set_up(self):
# Create app using WebTest
settings = get_appsettings('test.ini', name='main')
app = main({}, **settings)
self.test_app = TestApp(app)
# Don't set up an additional session in the tests. Instead import
# and use pyramid_sqlalchemy.Session, which is set up in the application.
def base_tear_down(self):
Session.remove()
class MyTests(BaseTestCase):
def setUp(self):
self.base_set_up()
with transaction.manager:
self.user = User('user@email.com', 'John', 'Smith')
Session.add(self.user)
Session.flush()
Session.expunge_all()
Session.expire_all() # "Reset" your session.
def tearDown(self):
self.base_tear_down()