Writing tests

unittest test cases

pyramid_sqlalchemy provides a DatabaseTestCase class which can be used when writing unit or integration tests that require a working database. You can use this as base class for you test classes. This example updates the Pyramid integration test example to add database support.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from pyramid_sqlalchemy.testing import DatabaseTestCase
from pyramid import testing

class ViewIntegrationTests(DatabaseTestCase):
    def setUp(self):
        super(ViewIntegrationTests, self).setUp()
        self.config = testing.setUp()
        self.config.include('myapp')

    def tearDown(self):
        testing.tearDown()
        super(ViewIntegrationTests, self).tearDown()

Warning

It is critical that you call the setUp() method of the base classes first. This is necessary to guarantee everything is configured correctly before you run any code that might touch pyramid_sqlalchemy.

Writing functional tests is just as easy. The example below is a modified version of the Pyramid functional test example.

1
2
3
4
5
6
7
8
9
from pyramid_sqlalchemy.testing import DatabaseTestCase

class FunctionalTests(unittest.TestCase):
    def setUp(self):
        from myapp import main
        from webtest import TestApp
        super(ViewIntegrationTests, self).setUp()
        app = main({})
        self.testapp = TestApp(App)

Normally all tests are run with an in-memory SQLite database. There are several ways to change this:

  • Set the db_uri variable in your test class to a different database URI.
  • Set the DB_URI environment variable.
  • If you use pytest as test runner you can use the --sql-url option to set the database URI.

py.test fixtures

If you use pytest you can use the test fixtures provided by pyramid_sqlalchemy.

For tests that need an active connection, but that do not need to use SQLAlchemy there is a transaction fixture available. This fixture creates a new transaction that will automatically be aborted at the end of the test. In order to prevent the transaction from being committed accidentally it is marked as doomed: this will turn any call to commit() into an error.

1
2
3
@pytest.mark.usefixtures('transaction')
def test_transaction_integration():
    # Test code that needs a transaction

The sql_session fixture must be used to test any code that needs to access a database. This fixture will setup a SQL backend and create all known tables. To speed up tests this will only be done once for the py.test session. Each test itself is running within its own transaction, to guarantee that any database changes are reverted after the test, and the next test starts with a clean database.

1
2
3
4
5
def test_model_sets_id_automatically(sql_session):
    obj = Account(login='jane')
    sql_session.add(obj)
    sql_session.flush()
    assert obj.id is not None

Normally all tests will use an in-memory SQLite database. You can run your tests with a different backend by using the --sql-url=<url> commandline option. For example to run all tests against a local PostgreSQL server using the pytest database:

$ bin/py.test --sql-url=postgresql:///pytest

There is also a --sql-echo commandline option which will echo all executed SQL statements to the console. This must be used in combination with pytests’ -s option to make the console output visisble.

$ bin/py.test --sql-echo -s
======================================= test session starts ========================================
platform darwin -- Python 2.7.8 -- py-1.4.20 -- pytest-2.5.2
plugins: pyramid-sqlalchemy
collected 36 items / 3 skipped

tests/ext/test_sql.py 2014-08-30 09:02:38,070 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2014-08-30 09:02:38,070 INFO sqlalchemy.engine.base.Engine ()
2014-08-30 09:02:38,070 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1

Using the provided fixtures you can create a new fixture for functional tests. This fixture needs add a special key to the request environment to tell the pyramid_tm tween not to to create or commit transactions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pytest
from webtest import TestApp
from myapp import main

@pytest.fixture
def app(monkeypatch, sql_session):
    # Prevent SQL re-configuration with non-testing setup
    monkeypatch.setattr('pyramid_sqlalchemy.includeme', lambda c: None)
    app = main({})
    return TestApp(app, extra_environ={'repoze.tm.active': True})