From 2edeac0d8329ab373375de20d21d80d316240bca Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 27 Aug 2024 19:10:50 -0500 Subject: [PATCH] fix: add common `DataTestCase` for use in other packages --- src/wuttjamaican/testing.py | 97 ++++++++++++++++++++++++++++++++----- tests/test_app.py | 4 +- tests/test_conf.py | 8 +-- tests/test_people.py | 30 +++--------- 4 files changed, 98 insertions(+), 41 deletions(-) diff --git a/src/wuttjamaican/testing.py b/src/wuttjamaican/testing.py index a51f3b5..cb81ad7 100644 --- a/src/wuttjamaican/testing.py +++ b/src/wuttjamaican/testing.py @@ -30,22 +30,27 @@ import tempfile import warnings from unittest import TestCase +from wuttjamaican.conf import WuttaConfig -class FileConfigTestCase(TestCase): + +class FileTestCase(TestCase): """ - Common base class for test suites which write temporary files, for - sake of testing the config constructor etc. + Base class for test suites which (may) write temporary files, for + sake of testing the config constructor etc. It inherits from + :class:`python:unittest.TestCase`. - This inherits from :class:`python:unittest.TestCase` and adds the - following features: - - Creates a temporary folder on setup, and removes it on teardown. - Adds the :meth:`write_file()` method to help with creating - temporary files. + This class creates a temporary folder on setup, and removes it on + teardown. See below for features exposed to work with the folder. .. attribute:: tempdir Path to the temporary folder created during setup. + + .. note:: + + If you subclass this and need to override setup/teardown, + please be sure to call the corresponding methods for this + class. """ def setUp(self): @@ -54,8 +59,6 @@ class FileConfigTestCase(TestCase): def setup_files(self): """ - Setup logic specific to the ``FileConfigTestCase``. - This creates the temporary folder. """ self.tempdir = tempfile.mkdtemp() @@ -73,8 +76,6 @@ class FileConfigTestCase(TestCase): def teardown_files(self): """ - Teardown logic specific to the ``FileConfigTestCase``. - This removes the temporary folder. """ shutil.rmtree(self.tempdir) @@ -105,3 +106,73 @@ class FileConfigTestCase(TestCase): Note that this will be created *underneath* :attr:`tempdir`. """ return tempfile.mkdtemp(dir=self.tempdir) + + +# TODO: deprecate / remove this +FileConfigTestCase = FileTestCase + + +class DataTestCase(FileTestCase): + """ + Base class for test suites requiring a full (typical) database. + + It inherits from :class:`FileTestCase` so also has the + file-related methods. + + This uses a SQLite in-memory database and creates all tables for + the app model. The running test has these attributes: + + .. attribute:: config + + Reference to the config object. + + .. attribute:: app + + Reference to the app handler. + + .. attribute:: session + + Open session for the test DB. + + .. note:: + + If you subclass this and need to override setup/teardown, + please be sure to call the corresponding methods for this + class. + + However you do *not* need to call the file-related setup or + teardown methods, as this class handles that automatically. + """ + + def setUp(self): + """ """ + self.setup_db() + + def setup_db(self): + """ + Perform config/app/db setup operations for the test. + """ + self.setup_files() + self.config = self.make_config(defaults={ + 'wutta.db.default.url': 'sqlite://', + }) + self.app = self.config.get_app() + + # init db + model = self.app.model + model.Base.metadata.create_all(bind=self.config.appdb_engine) + self.session = self.app.make_session() + + def tearDown(self): + """ """ + self.teardown_db() + + def teardown_db(self): + """ + Perform config/app/db teardown operations for the test. + """ + self.teardown_files() + + def make_config(self, **kwargs): + """ """ + return WuttaConfig(**kwargs) diff --git a/tests/test_app.py b/tests/test_app.py index cf16d84..54ab540 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -15,10 +15,10 @@ from wuttjamaican import app from wuttjamaican.progress import ProgressBase from wuttjamaican.conf import WuttaConfig from wuttjamaican.util import UNSPECIFIED -from wuttjamaican.testing import FileConfigTestCase +from wuttjamaican.testing import FileTestCase -class TestAppHandler(FileConfigTestCase): +class TestAppHandler(FileTestCase): def setUp(self): self.setup_files() diff --git a/tests/test_conf.py b/tests/test_conf.py index fb9e730..89af3be 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -10,10 +10,10 @@ import pytest from wuttjamaican import conf from wuttjamaican.exc import ConfigurationError from wuttjamaican.app import AppHandler -from wuttjamaican.testing import FileConfigTestCase +from wuttjamaican.testing import FileTestCase -class TestWuttaConfig(FileConfigTestCase): +class TestWuttaConfig(FileTestCase): def test_contstructor_basic(self): config = conf.WuttaConfig() @@ -505,7 +505,7 @@ class TestGenericDefaultFiles(TestCase): self.assertEqual(len(files), 0) -class TestGetConfigPaths(FileConfigTestCase): +class TestGetConfigPaths(FileTestCase): def test_winsvc(self): myconf = self.write_file('my.conf', """ @@ -523,7 +523,7 @@ winsvc.RattailFileMonitor = /path/to/other/file self.assertEqual(files, []) -class TestMakeConfig(FileConfigTestCase): +class TestMakeConfig(FileTestCase): # nb. we use appname='wuttatest' in this suite to avoid any # "valid" default config files, env vars etc. which may be present diff --git a/tests/test_people.py b/tests/test_people.py index c2cd2a1..0ced117 100644 --- a/tests/test_people.py +++ b/tests/test_people.py @@ -1,9 +1,7 @@ # -*- coding: utf-8; -*- -from unittest import TestCase - from wuttjamaican import people as mod -from wuttjamaican.conf import WuttaConfig +from wuttjamaican.testing import DataTestCase try: import sqlalchemy as sa @@ -12,41 +10,29 @@ except ImportError: else: - class TestPeopleHandler(TestCase): + class TestPeopleHandler(DataTestCase): - def setUp(self): - self.config = WuttaConfig() - self.app = self.config.get_app() - self.handler = mod.PeopleHandler(self.config) - - self.engine = sa.create_engine('sqlite://') - self.app.model.Base.metadata.create_all(bind=self.engine) - self.session = self.make_session() - - def tearDown(self): - self.session.close() - self.app.model.Base.metadata.drop_all(bind=self.engine) - - def make_session(self): - return self.app.make_session(bind=self.engine) + def make_handler(self): + return mod.PeopleHandler(self.config) def test_get_person(self): model = self.app.model myperson = model.Person(full_name='Barny Rubble') self.session.add(myperson) self.session.commit() + handler = self.make_handler() # empty obj is ignored - person = self.handler.get_person(None) + person = handler.get_person(None) self.assertIsNone(person) # person is returned as-is - person = self.handler.get_person(myperson) + person = handler.get_person(myperson) self.assertIs(person, myperson) # find person from user myuser = model.User(username='barney', person=myperson) self.session.add(myuser) self.session.commit() - person = self.handler.get_person(myuser) + person = handler.get_person(myuser) self.assertIs(person, myperson)