Python 3.x 如何为每个单元测试正确设置一个SQLAlchemy会话?

Python 3.x 如何为每个单元测试正确设置一个SQLAlchemy会话?,python-3.x,sqlalchemy,pyramid,webtest,Python 3.x,Sqlalchemy,Pyramid,Webtest,当使用WebTest测试我的金字塔应用程序时,我无法在测试中创建/使用单独的会话,而不会收到关于已存在作用域会话的警告 下面是金字塔应用程序的main()函数,它是配置数据库的地方 # __init__.py of Pyramid application from pyramid_sqlalchemy import init_sqlalchemy from sqlalchemy import create_engine def main(global_config, **settings)

当使用WebTest测试我的金字塔应用程序时,我无法在测试中创建/使用单独的会话,而不会收到关于已存在作用域会话的警告

下面是金字塔应用程序的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个警告,其中除第一个测试外的每个测试都会给出以下警告:

从金字塔应用程序:
\uuuuu init\uuuuuuuuuuuupy.py->main->init\usqlalchemy(引擎)
: sqlalchemy.exc.SAWarning:至少已存在一个作用域会话。configure()不能影响已创建的会话

如果这有任何用处,我相信我在这里看到了同样的问题,除了我使用金字塔炼金术而不是创建我自己的数据库会话


回答我自己的问题:我不确定这是否是最好的方法,但对我来说是有效的

我没有尝试在测试中创建单独的会话,而是使用pyramid_sqlalchemy会话工厂,该工厂在应用程序中配置。据我所知,在测试代码和应用程序代码中调用
Session
都会返回相同的已注册作用域_会话

我为测试创建单独会话的最初目的是确认记录正在写入数据库,而不仅仅是在活动的SQLAlchemy会话中更新。使用这种新方法,我通过在测试事务和应用程序事务之间转换的测试点发出
Session.expire\u all()
来避免这些“缓存”问题

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

你能给我们一个出现警告的测试的代码吗更新帖子以包含我找到的更多信息。我无法用你链接的代码重现警告,你显然是在调用Session.configure()连续两次,但这些调用没有出现在您向我们展示的内容中,并且在init_sqlalchemy中没有此类调用,我也不理解为什么您调用main来设置引擎和会话,然后在基本设置中设置第二个引擎到同一个db和会话,因为您刚刚覆盖了第一个引擎,可能尝试删除主服务器中的会话配置,或者干脆不在测试中调用它。@bboumend感谢您在这方面所做的努力。我相信要重现这个问题,您需要在应用程序和测试中使用会话。我最初希望设置单独的会话来测试对数据库的实际更改,因为会话中的状态可能与某些(意外)情况下实际提交的状态不同。但是,我接受了您的建议,正在重用同一个会话,并使用
session.expire\u all()
根据需要清除状态。