Я не понимаю, почему загруженный объект в SQLAlchemy не обновляется после обновления базы данных (и session.commit()
) в одной версии теста, а не в другой. Ссылочный объект также все еще прикреплен к сеансу в рабочей версии, но имеет Session.object_session(object) == None
в нерабочей версии. Я использую приспособления pytest для настройки среды сеансов и приложений, и у меня есть clean_*
их версии, которые я использую, потому что существуют версии этих приспособлений, которые ожидают, что база данных также не будет пустой. Вот код (сначала исправления для контекста, затем рабочие и нерабочие тесты)
приборы:
@pytest.fixture(scope="session")
def postgresql(request, logger):
p = testing.postgresql.Postgresql()
logger.debug(p.url())
def fin():
logger.debug("teardown DB")
p.stop()
request.addfinalizer(fin)
return p
@pytest.fixture()
def clean_app(request, clean_postgresql):
test_config = TestConfig()
test_config.SQLALCHEMY_DATABASE_URI = clean_postgresql.url()
app = create_app(test_config, settings_override={
"SQLALCHEMY_DATABASE_URI": clean_postgresql.url(),
"TOKEN_SECRET_KEY": "secret"
})
ctx = app.app_context()
ctx.push()
app.db = db
db.create_all()
def fin():
ctx.pop()
request.addfinalizer(fin)
return app
@pytest.fixture()
def clean_session(app, request):
connection = app.db.engine.connect()
transaction = connection.begin()
options = dict(bind=connection, binds={})
session = app.db.create_scoped_session(options=options)
original_session = app.db.session
app.db.session = session
def fin():
transaction.rollback()
session.remove()
app.db.session = original_session
request.addfinalizer(fin)
return session
@pytest.fixture()
def clean_client(clean_app):
client = clean_app.test_client()
yield client
@pytest.fixture()
def make_foo():
"""Factory to make a foo object for tests"""
def _make_foo(params={}):
foo_params = dict(FOO_DEFAULTS, **params)
return Foo.create(foo_params)
yield _make_user
тест с закомментированным рабочим примером (класс Foo имеет отношение один-ко-многим с классом Bar):
def test_example(request, clean_app, clean_session, clean_client, make_object):
foo = make_foo()
foo.create_new_bar()
assert len(foo.bars) == 1 #True
response = clean_client.put(
"/api/foos/{1}?action=create_bar".format(foo.id),
headers={'Authorization': 'Bearer <Valid Token>'},
json={'barAttribute': 18},
# data=json.dumps({'barAttribute': 18}),
# content_type='application/json'
)
assert response.status == 200 #True
print(Session.object_session(foo)) #This prints `None` with current code
assert len(foo.bars) == 2 #This fails with 1 != 2 with current code
Если я закомментирую строку json=
и раскомментирую две нижние строки, код сработает, а Session.object_session(foo)
выведет объект sqlalchemy.orm.session.SignallingSession
, как и ожидалось.
Я явно не совсем понимаю, где-нибудь используется сеанс SQLAlchemy.