Thanks Michael,
I’d be glad to have this appear in a cookiecutter – let me know how I can help.
For the record, what I missed to mention (but is actually the bigger selling
point for me):
Apart from the automatic rollback functionality, that approach allows putting
anything to the session, no matter where. I.e. you can `add` and `flush`
something in a test, before calling the `TestApp` in the same test, which will
have access to what was `add`ed to that session. Still without anything being
committed. You can imagine how easy writing integration tests is in the end.
Best, Andi
From: <[email protected]> on behalf of Michael Merickel
<[email protected]>
Reply-To: <[email protected]>
Date: Tuesday, 6. November 2018 at 16:58
To: <[email protected]>
Subject: Re: [pylons-discuss] Understanding how to perform functional testing
using transactions with pyramid + SQLAlchemy + pytest + webtest for views that
use a postgreSQL database.
Andi, I think this is a fantastic approach. The key here is to override
request.dbsession to a mocked out version which is not connected to pyramid_tm
at all, giving you full control of commit/rollback from the outside. One extra
step would be to disable pyramid_tm entirely by setting environ['tm.active'] =
True which would let you mock out request.tm if you were using it for any other
purposes. This can be done from the TestApp constructor (you could also use
this for the dbsession to avoid using the get_current_request threadlocal) via
TestApp(app, extra_environ={'tm.active': True, 'dbsession': dbsession}).
We should look at adding this pattern to the cookiecutter to make it more
accessible for people.
On Tue, Nov 6, 2018 at 12:54 AM 'andi' via pylons-discuss
<[email protected]> wrote:
Hi dcs3spp,
I remember it was pretty complicated for me to setup something as you describe:
one sqla session per test, which will be automatically rolled back.
You are right, that in the TestApp there is a separate transaction handling,
which destroys your approach. To solve that problem for me (no idea if this was
elegant or not, but it does what you describe) I did the following.
Created a TestApp with a custom registry field:
@pytest.fixture(scope="function")
def _test_app(router: Router) -> TestApp:
return TestApp(router)
@pytest.fixture(scope="function")
def test_app(_test_app: TestApp, _sqla_session) -> TestApp:
"""wrapper to ensure the fixture-created `sqla_session` will be picked up
in `test_app`"""
_test_app.app.registry.settings['paste.testing'] = True
_test_app.app.registry['paste.testing.session'] = _sqla_session
return _test_app
along with the usual sqla fixure
@pytest.fixture(scope="session")
def _sqla_session(pyramid_config, sqla_engine) -> Session:
"""
Depending on this fixture is comparable to a integration test, which has
nothing more than
the orm properly defined. Which is helpful, but not the full application
configured.
"""
session_factory = get_session_factory(sqla_engine, db_session)
return session_factory()
@pytest.fixture(scope="function")
def sqla_session(_sqla_session: Session, test_app):
"""
wrap a transaction
"""
# TODO andi: magically there is a transaction active here. why.
# t = _sqla_session.begin()
yield _sqla_session
# this is the important `rollback`
_sqla_session.transaction.rollback()
Then I have something, that probably `pyramid_tm` does under the hood: a
request listener, which handles a “per request session”.
def add_tm_session(req):
# this property is set in `webtest.app.TestApp#do_request`
if 'paste.testing' in req.environ and req.environ['paste.testing'] is True:
from pyramid.threadlocal import get_current_registry
registry = get_current_registry()
# in case of integration testing, we set this registry key with the
`sqla_session` from
# around. this allows us to operate in the same session, so we don't
need any commits.
return registry.get('paste.testing.session', None)
# request.tm is the transaction manager used by pyramid_tm
return get_tm_session(session_factory, req.tm)
# make request.dbsession available for use in Pyramid
config.add_request_method(add_tm_session, 'dbsession', reify=True)
You can see the “magic” in there.
What I found in my documentation was this link (another was dead already),
which I found helpful: https://gist.github.com/inklesspen/4504383
The good thing about this pattern for me was, that I could apply this to Celery
jobs in tests or Spyne integration - aka pretty flexible.
Hope this helps.
Andi
On 5. Nov 2018, at 19:37, 'dcs3spp' via pylons-discuss
<[email protected]> wrote:
Hi,
I am a newbie having difficulty understanding and getting functional testing
working with pyramid, SQLAlchemy, pytest and webtest. I am using pyramid 1.10.
Hoping that someone is able to advise a way forward or direct me to any useful
resources.
I have written the fixture below that creates a SQL Alchemy session for each
test and initialises data within a transaction, based upon documentation for
functional testing at the wiki that uses unittest.
When the fixture completes the transaction aborts and closes the session. When
the next test runs the fixture will create a new transaction and reinitialise
the data.
@pytest.fixture
def session(request, testapp):
factory = testapp.app.registry['dbsession_factory']
engine = factory.kw['bind']
# create all the tables in db
Base.metadata.create_all(engine)
log.info ("Creating root transaction for the test session")
with transaction.manager as tx:
from plantoeducate_data.models import get_tm_session
session=get_tm_session(factory, transaction.manager)
brief=DocumentTemplateModel ()
brief.DocumentDescription='brief'
brief.DocumentTypeID=1
brief.DocumentFilePath='brief.ott'
feedback=DocumentTemplateModel ()
feedback.DocumentDescription='feedback'
feedback.DocumentTypeID=2
feedback.DocumentFilePath='feedback.ott'
session.add_all([brief, feedback])
#session.flush()
yield session
log.info("Rolling back root transaction")
transaction.abort()
session.close()
I have two tests that use the fixture, listed below:
def test_delete_document(self, testapp, session):
doc=session.query(DocumentTemplateModel).first()
import pdb; pdb.set_trace()
# delete the document
res = testapp.delete('/documents/templates/{}'.format(doc.DocumentID),
status=204)
def test_filter_documents(self, testapp, session):
res = testapp.get('/documents/templates/1', status=200)
expectedTemplatesCount = 2
import pdb; pdb.set_trace()
I think that pyramid_tm creates a session from a SQLAlchemy session factory for
each request and hooks up the current active transaction.
When the first test is run I can see data in the session, however the request
session in the view does not see the data.
When the second test runs there is no data at all in the session that was
created by the test fixture.
How do I make the data initialised in the test visible to the view being
tested? Is it possible to perform testing by initialising data in SQLAlchemy,
making it visible to the request session in the view and then rolling back
state in preparation for subsequent test?
Kind Regards
dcs3spp
--
You received this message because you are subscribed to the Google Groups
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/pylons-discuss/bf247ef1-1a48-4934-9bce-205d166a5c64%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/pylons-discuss/A1460691-D307-4B5C-846C-E8CD20BE8FA3%40googlemail.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/pylons-discuss/CAKdhhwFcagH3GzLMdJZ2qCa%3D9csv5nEzcRR47%2Bg5k%2BYFMH1kRA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/pylons-discuss/5929CFD4-982F-44E0-A02F-71E048E1D6A6%40googlemail.com.
For more options, visit https://groups.google.com/d/optout.