From 3623f35b75231d05fb67406aeec0681b35f96480 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 9 Jan 2021 08:12:05 -0800 Subject: Update database step in Quick Tour --- docs/quick_tour/sqla_demo/tests/conftest.py | 132 +++++++++++++++++++++ docs/quick_tour/sqla_demo/tests/test_functional.py | 13 ++ docs/quick_tour/sqla_demo/tests/test_it.py | 66 ----------- docs/quick_tour/sqla_demo/tests/test_views.py | 23 ++++ 4 files changed, 168 insertions(+), 66 deletions(-) create mode 100644 docs/quick_tour/sqla_demo/tests/conftest.py create mode 100644 docs/quick_tour/sqla_demo/tests/test_functional.py delete mode 100644 docs/quick_tour/sqla_demo/tests/test_it.py create mode 100644 docs/quick_tour/sqla_demo/tests/test_views.py (limited to 'docs/quick_tour/sqla_demo/tests') diff --git a/docs/quick_tour/sqla_demo/tests/conftest.py b/docs/quick_tour/sqla_demo/tests/conftest.py new file mode 100644 index 000000000..641527ca1 --- /dev/null +++ b/docs/quick_tour/sqla_demo/tests/conftest.py @@ -0,0 +1,132 @@ +import alembic +import alembic.config +import alembic.command +import os +from pyramid.paster import get_appsettings +from pyramid.scripting import prepare +from pyramid.testing import DummyRequest, testConfig +import pytest +import transaction +import webtest + +from sqla_demo import main +from sqla_demo import models +from sqla_demo.models.meta import Base + + +def pytest_addoption(parser): + parser.addoption('--ini', action='store', metavar='INI_FILE') + +@pytest.fixture(scope='session') +def ini_file(request): + # potentially grab this path from a pytest option + return os.path.abspath(request.config.option.ini or 'testing.ini') + +@pytest.fixture(scope='session') +def app_settings(ini_file): + return get_appsettings(ini_file) + +@pytest.fixture(scope='session') +def dbengine(app_settings, ini_file): + engine = models.get_engine(app_settings) + + alembic_cfg = alembic.config.Config(ini_file) + Base.metadata.drop_all(bind=engine) + alembic.command.stamp(alembic_cfg, None, purge=True) + + # run migrations to initialize the database + # depending on how we want to initialize the database from scratch + # we could alternatively call: + # Base.metadata.create_all(bind=engine) + # alembic.command.stamp(alembic_cfg, "head") + alembic.command.upgrade(alembic_cfg, "head") + + yield engine + + Base.metadata.drop_all(bind=engine) + alembic.command.stamp(alembic_cfg, None, purge=True) + +@pytest.fixture(scope='session') +def app(app_settings, dbengine): + return main({}, dbengine=dbengine, **app_settings) + +@pytest.fixture +def tm(): + tm = transaction.TransactionManager(explicit=True) + tm.begin() + tm.doom() + + yield tm + + tm.abort() + +@pytest.fixture +def dbsession(app, tm): + session_factory = app.registry['dbsession_factory'] + return models.get_tm_session(session_factory, tm) + +@pytest.fixture +def testapp(app, tm, dbsession): + # override request.dbsession and request.tm with our own + # externally-controlled values that are shared across requests but aborted + # at the end + testapp = webtest.TestApp(app, extra_environ={ + 'HTTP_HOST': 'example.com', + 'tm.active': True, + 'tm.manager': tm, + 'app.dbsession': dbsession, + }) + + return testapp + +@pytest.fixture +def app_request(app, tm, dbsession): + """ + A real request. + + This request is almost identical to a real request but it has some + drawbacks in tests as it's harder to mock data and is heavier. + + """ + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + + # without this, request.dbsession will be joined to the same transaction + # manager but it will be using a different sqlalchemy.orm.Session using + # a separate database transaction + request.dbsession = dbsession + request.tm = tm + + yield request + +@pytest.fixture +def dummy_request(tm, dbsession): + """ + A lightweight dummy request. + + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: + + - It does not have request extensions applied. + - Threadlocals are not properly pushed. + + """ + request = DummyRequest() + request.host = 'example.com' + request.dbsession = dbsession + request.tm = tm + + return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/quick_tour/sqla_demo/tests/test_functional.py b/docs/quick_tour/sqla_demo/tests/test_functional.py new file mode 100644 index 000000000..4045871da --- /dev/null +++ b/docs/quick_tour/sqla_demo/tests/test_functional.py @@ -0,0 +1,13 @@ +from sqla_demo import models + +def test_my_view_success(testapp, dbsession): + model = models.MyModel(name='one', value=55) + dbsession.add(model) + dbsession.flush() + + res = testapp.get('/', status=200) + assert res.body + +def test_notfound(testapp): + res = testapp.get('/badurl', status=404) + assert res.status_code == 404 diff --git a/docs/quick_tour/sqla_demo/tests/test_it.py b/docs/quick_tour/sqla_demo/tests/test_it.py deleted file mode 100644 index f0848564b..000000000 --- a/docs/quick_tour/sqla_demo/tests/test_it.py +++ /dev/null @@ -1,66 +0,0 @@ -import unittest - -from pyramid import testing - -import transaction - - -def dummy_request(dbsession): - return testing.DummyRequest(dbsession=dbsession) - - -class BaseTest(unittest.TestCase): - def setUp(self): - self.config = testing.setUp(settings={ - 'sqlalchemy.url': 'sqlite:///:memory:' - }) - self.config.include('sqla_demo.models') - settings = self.config.get_settings() - - from sqla_demo.models import ( - get_engine, - get_session_factory, - get_tm_session, - ) - - self.engine = get_engine(settings) - session_factory = get_session_factory(self.engine) - - self.session = get_tm_session(session_factory, transaction.manager) - - def init_database(self): - from sqla_demo.models.meta import Base - Base.metadata.create_all(self.engine) - - def tearDown(self): - from sqla_demo.models.meta import Base - - testing.tearDown() - transaction.abort() - Base.metadata.drop_all(self.engine) - - -class TestMyViewSuccessCondition(BaseTest): - - def setUp(self): - super().setUp() - self.init_database() - - from sqla_demo.models import MyModel - - model = MyModel(name='one', value=55) - self.session.add(model) - - def test_passing_view(self): - from sqla_demo.views.default import my_view - info = my_view(dummy_request(self.session)) - self.assertEqual(info['one'].name, 'one') - self.assertEqual(info['project'], 'sqla_demo') - - -class TestMyViewFailureCondition(BaseTest): - - def test_failing_view(self): - from sqla_demo.views.default import my_view - info = my_view(dummy_request(self.session)) - self.assertEqual(info.status_int, 500) diff --git a/docs/quick_tour/sqla_demo/tests/test_views.py b/docs/quick_tour/sqla_demo/tests/test_views.py new file mode 100644 index 000000000..b98d9af61 --- /dev/null +++ b/docs/quick_tour/sqla_demo/tests/test_views.py @@ -0,0 +1,23 @@ +from sqla_demo import models +from sqla_demo.views.default import my_view +from sqla_demo.views.notfound import notfound_view + + +def test_my_view_failure(app_request): + info = my_view(app_request) + assert info.status_int == 500 + +def test_my_view_success(app_request, dbsession): + model = models.MyModel(name='one', value=55) + dbsession.add(model) + dbsession.flush() + + info = my_view(app_request) + assert app_request.response.status_int == 200 + assert info['one'].name == 'one' + assert info['project'] == 'sqla_demo' + +def test_notfound_view(app_request): + info = notfound_view(app_request) + assert app_request.response.status_int == 404 + assert info == {} -- cgit v1.2.3