[pyramid] testing views with models sqlalchemy

287 views
Skip to first unread message

Whitiz

unread,
Jul 21, 2011, 6:21:30 PM7/21/11
to pylons-discuss
I've tried several approaches to do unit testing of views that have
Model access sqlalchemy,
but nothing seems to work.
A problem always arises when running the second test.
I am using the internal memory 'sqlite://'
The first test works, because I need initialized models prior to call
calling the tests the second test fails because drop_all and/or
rollback to do not appears to work and the models can not be
recreated.
Below is example code based on the tutorial with sqlalchemy.
with this code it fails with
IntegrityError: (IntegrityError) column name is not unique u'INSERT
INTO models (name, value) VALUES (?, ?)' (u'root', 55)
while running the second test.

import unittest
from pyramid.config import Configurator
from pyramid import testing
from tutorial.models import MyModel, DBSession, Base
import transaction

def _initTestingDB():
print "initTestingDB"
from sqlalchemy import create_engine
engine = create_engine('sqlite://')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
create_models(DBSession)

return DBSession

def create_models(dbSession):
session = dbSession()
model = MyModel(name=u'root', value=55)
session.add(model)
session.flush()
transaction.commit()

def clear_models(dbSession):
session = dbSession()
old_object = session.query(MyModel)
session.delete(old_object)
session.flush()
transaction.commit()
print "clear_models"

class TestMyView(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
self.dbSession=_initTestingDB()

def tearDown(self):
testing.tearDown()
clear_models(self.dbSession)
print "rollback"
self.dbSession.rollback()

def test_it(self):
from tutorial.views import my_view
request = testing.DummyRequest()
info = my_view(request)
self.assertEqual(info['root'].name, 'root')
self.assertEqual(info['project'], 'tutorial')

def test_it2(self):
from tutorial.views import my_view
request = testing.DummyRequest()
info = my_view(request)
self.assertEqual(info['root'].name, 'root')
self.assertEqual(info['project'], 'tutorial')

Kiss György

unread,
Jul 22, 2014, 2:28:18 AM7/22/14
to pylons-...@googlegroups.com, schug...@gmail.com
Basically I have the same question. The example test running fine, but I want to use py.test for testing, so I rewrote it to py.test style fixtures.

This is the original example, which is running fine:

import unittest
import transaction
from pyramid import testing
from .models import DBSession


class TestMyViewSuccessCondition(unittest.TestCase):
    def setUp(self):
        self.config = testing.setUp()
        from sqlalchemy import create_engine
        engine = create_engine('sqlite://')
        from .models import (
            Base,
            MyModel,
            )
        DBSession.configure(bind=engine)
        Base.metadata.create_all(engine)
        with transaction.manager:
            model = MyModel(name='one', value=55)
            DBSession.add(model)

    def tearDown(self):
        DBSession.remove()
        testing.tearDown()

    def test_passing_view(self):
        from .views import my_view
        request = testing.DummyRequest()
        info = my_view(request)
        self.assertEqual(info['one'].name, 'one')
        self.assertEqual(info['project'], 'pokerherd')


class TestMyViewFailureCondition(unittest.TestCase):
    def setUp(self):
        self.config = testing.setUp()
        from sqlalchemy import create_engine
        engine = create_engine('sqlite://')
        from .models import (
            Base,
            MyModel,
            )
        DBSession.configure(bind=engine)

    def tearDown(self):
        DBSession.remove()
        testing.tearDown()

    def test_failing_view(self):
        from .views import my_view
        request = testing.DummyRequest()
        info = my_view(request)
        self.assertEqual(info.status_int, 500)


py.test style rewrite:

# conftest.py
from pyramid import testing
import pytest
import transaction
from pokerherd.models import DBSession


@pytest.fixture
def req():
    """Pyramid DummyRequest."""
    return testing.DummyRequest()

@pytest.fixture(scope='function')
def db(request):
    """SQLAlchemy session."""
    config = testing.setUp()
    from sqlalchemy import create_engine
    engine = create_engine('sqlite://', echo=True)
    from pokerherd.models import Base, MyModel
    DBSession.configure(bind=engine)
    Base.metadata.create_all(engine)
    with transaction.manager:
        model = MyModel(name='one', value=55)
        DBSession.add(model)

    def fin():
        DBSession.remove()
        testing.tearDown()
    request.addfinalizer(fin)

    return DBSession


# test_views.py
from myproject.models import DBSession, MyModel


class TestMyView:
    def test_passing_view(self, db, req):
        from myproject.views import my_view
        response = my_view(req)

        assert response['one'].name == 'one'
        assert response['project'] == 'pokerherd'

    def test_failing_view(self, req):
        from myproject.views import my_view
        response = my_view(req)

        assert response.status_int == 500

The first example is working fine, but the second test fails, because sqlite DBSession from memory don't get deleted.
I don't understand the difference.

If I call Base.metadata.drop_all(engine) in the finalizer, the test passes, but in the original test, only the session is removed and still working fine.
Also in SQLAlchemy manual, scoped DBSession.remove() is described as:
"Dispose of the current Session, if present. This will first call Session.close() method on the current Session, which releases any existing transactional/connection resources still being held; transactions specifically are rolled back. The Session is then discarded. Upon next usage within the same scope, the scoped_session will produce a new Session object."

What's happening here?

Wichert Akkerman

unread,
Jul 22, 2014, 1:14:53 PM7/22/14
to pylons-...@googlegroups.com, schug...@gmail.com
You may need to explicitly dispose of the engine. I would recommend to look at the pytest section of the pyramid_sqlalchemy documentation: https://pyramid-sqlalchemy.readthedocs.org/en/latest/tests.html#py-test-fixtures . That describes in detail how you can setup pytest fixtures for SQLAlchemy, and use this with functional tests of a Pyramid application.

Wichert.

Walkman

unread,
Jul 24, 2014, 9:39:21 AM7/24/14
to pylons-...@googlegroups.com
Thanks! engine.dispose() worked fine and that page helped me a lot. I did not know about pyramid-sqlalchemy.
I even implemented automatic transaction handling based on those described by it.
Anyone want to use py.test with pyramid I recommend at least reading pyramid-sqlalchemy documentation!
Thanks again!

----
Walkman
Reply all
Reply to author
Forward
0 new messages