From 8d090b849fb3bd4a169c7fecfd74f44c198346f0 Mon Sep 17 00:00:00 2001 From: Alex Burke Date: Tue, 25 Mar 2025 10:23:03 +0100 Subject: [PATCH] Embellish fixture assertions with the path. --- tests/support/__init__.py | 85 +++++++++++++++++++++- tests/test_mig_shared_configuration.py | 7 +- tests/test_mig_shared_functionality_cat.py | 15 ++-- 3 files changed, 93 insertions(+), 14 deletions(-) diff --git a/tests/support/__init__.py b/tests/support/__init__.py index 422182b4a..3fc25de34 100644 --- a/tests/support/__init__.py +++ b/tests/support/__init__.py @@ -46,6 +46,21 @@ from tests.support._env import MIG_ENV, PY2 +# Alow the use of SimpleNamespace on PY2. + +if PY2: + class SimpleNamespace(dict): + """Bare minimum SimpleNamespace for Python 2.""" + + def __getattribute__(self, name): + if name == '__dict__': + return dict(**self) + + return self[name] +else: + from types import SimpleNamespace + + # Provide access to a configuration file for the active environment. if MIG_ENV in ('local', 'docker'): @@ -237,6 +252,9 @@ def assert_over(self, values=None, _AssertOver=AssertOver): self._register_check(check_callable) return assert_over + def temppath(self, relative_path, **kwargs): + return temppath(relative_path, self, **kwargs) + # custom assertions available for common use def assertFileContentIdentical(self, file_actual, file_expected): @@ -295,6 +313,69 @@ def pretty_display_path(absolute_path): assert not relative_path.startswith('..') return relative_path + def prepareFixtureAssert(self, fixture_relpath, fixture_format=None): + """Prepare to assert a value against a fixture.""" + + fixture_data, fixture_path = fixturefile( + fixture_relpath, fixture_format) + return SimpleNamespace( + assertAgainstFixture=lambda val: MigTestCase._assertAgainstFixture( + self, + fixture_format, + fixture_data, + fixture_path, + value=val + ), + copy_as_temp=lambda prefix: self._fixture_copy_as_temp( + self, + fixture_format, + fixture_data, + fixture_path, + prefix=prefix + ) + ) + + @staticmethod + def _assertAgainstFixture(testcase, fixture_format, fixture_data, fixture_path, value=None): + """Compare a value against fixture data ensuring that in the case of + failure the location of the fixture is prepended to the diff.""" + + assert value is not None + originalMaxDiff = testcase.maxDiff + testcase.maxDiff = None + + raised_exception = None + try: + testcase.assertEqual(value, fixture_data) + except AssertionError as diffexc: + raised_exception = diffexc + finally: + testcase.maxDiff = originalMaxDiff + if raised_exception: + message = "value differed from fixture stored at %s\n\n%s" % ( + _to_display_path(fixture_path), raised_exception) + raise AssertionError(message) + + @staticmethod + def _fixture_copy_as_temp(testcase, fixture_format, fixture_data, fixture_path, prefix=None): + """Copy a fixture to temporary file at the given path prefix.""" + + assert prefix is not None + fixture_basename = os.path.basename(fixture_path) + fixture_name = fixture_basename[0:-len(fixture_format) - 1] + normalised_path = fixturefile_normname(fixture_name, prefix=prefix) + copied_fixture_file = testcase.temppath(normalised_path) + shutil.copyfile(fixture_path, copied_fixture_file) + return copied_fixture_file + + +def _to_display_path(value): + """Convert a relative path to one to be shown as part of test output.""" + display_path = os.path.relpath(value, MIG_BASE) + if not display_path.startswith('.'): + return "./" + display_path + return display_path + def is_path_within(path, start=None, _msg=None): """Check if path is within start directory""" @@ -316,7 +397,7 @@ def ensure_dirs_exist(absolute_dir): return absolute_dir -def fixturefile(relative_path, fixture_format=None, include_path=False): +def fixturefile(relative_path, fixture_format=None): """Support function for loading fixtures from their serialised format. Doing so is a little more involved than it may seem because serialisation @@ -347,7 +428,7 @@ def fixturefile(relative_path, fixture_format=None, include_path=False): raise AssertionError( "unsupported fixture format: %s" % (fixture_format,)) - return (data, tmp_path) if include_path else data + return data, tmp_path def fixturefile_normname(relative_path, prefix=''): diff --git a/tests/test_mig_shared_configuration.py b/tests/test_mig_shared_configuration.py index e3132756a..a9609a12a 100644 --- a/tests/test_mig_shared_configuration.py +++ b/tests/test_mig_shared_configuration.py @@ -31,7 +31,7 @@ import os import unittest -from tests.support import MigTestCase, TEST_DATA_DIR, PY2, testmain, fixturefile +from tests.support import MigTestCase, TEST_DATA_DIR, PY2, testmain from mig.shared.configuration import Configuration @@ -71,7 +71,7 @@ def test_argument_wwwserve_max_bytes(self): @unittest.skipIf(PY2, "Python 3 only") def test_default_object(self): - expected_values = fixturefile( + prepared_fixture = self.prepareFixtureAssert( 'mig_shared_configuration--new', fixture_format='json') configuration = Configuration(None) @@ -84,8 +84,7 @@ def test_default_object(self): actual_values = _to_dict(configuration) - self.maxDiff = None - self.assertEqual(actual_values, expected_values) + prepared_fixture.assertAgainstFixture(actual_values) def test_object_isolation(self): configuration_1 = Configuration(None) diff --git a/tests/test_mig_shared_functionality_cat.py b/tests/test_mig_shared_functionality_cat.py index e02af8896..e8c332c77 100644 --- a/tests/test_mig_shared_functionality_cat.py +++ b/tests/test_mig_shared_functionality_cat.py @@ -35,7 +35,7 @@ import unittest from tests.support import MIG_BASE, PY2, TEST_DATA_DIR, MigTestCase, testmain, \ - fixturefile, fixturefile_normname, ensure_dirs_exist, temppath + temppath, ensure_dirs_exist from mig.shared.base import client_id_dir from mig.shared.functionality.cat import _main as submain, main as realmain @@ -78,13 +78,12 @@ def before_each(self): conf_user_db_home = ensure_dirs_exist(self.configuration.user_db_home) temppath(conf_user_db_home, self) - db_fixture, db_fixture_file = fixturefile('MiG-users.db--example', - fixture_format='binary', - include_path=True) - test_db_file = temppath(fixturefile_normname('MiG-users.db--example', - prefix=conf_user_db_home), - self) - shutil.copyfile(db_fixture_file, test_db_file) + prepared_fixture = self.prepareFixtureAssert( + 'MiG-users.db--example', + fixture_format='binary', + ) + + test_db_file = prepared_fixture.copy_as_temp(prefix=conf_user_db_home) # create the test user home directory self.test_user_dir = ensure_dirs_exist(test_user_dir)