diff --git a/tobiko/__init__.py b/tobiko/__init__.py index 9608bb9bb..6a1a13cd4 100644 --- a/tobiko/__init__.py +++ b/tobiko/__init__.py @@ -75,6 +75,7 @@ remove_fixture = _fixture.remove_fixture reset_fixture = _fixture.reset_fixture setup_fixture = _fixture.setup_fixture cleanup_fixture = _fixture.cleanup_fixture +use_fixture = _fixture.use_fixture list_required_fixtures = _fixture.list_required_fixtures SharedFixture = _fixture.SharedFixture FixtureManager = _fixture.FixtureManager diff --git a/tobiko/common/_fixture.py b/tobiko/common/_fixture.py index 1e749dd95..c77587de8 100644 --- a/tobiko/common/_fixture.py +++ b/tobiko/common/_fixture.py @@ -27,6 +27,7 @@ import tobiko from tobiko.common import _detail from tobiko.common import _deprecation from tobiko.common import _exception +from tobiko.common import _testcase LOG = log.getLogger(__name__) @@ -41,7 +42,7 @@ def is_fixture(obj): def get_fixture(obj: typing.Any, fixture_id: typing.Any = None, - manager: 'FixtureManager' = None): + manager: 'FixtureManager' = None) -> fixtures.Fixture: '''Returns a fixture identified by given :param obj: It returns registered fixture for given :param obj:. If none has been @@ -73,7 +74,7 @@ def get_fixture(obj: typing.Any, return manager.get_fixture(obj, fixture_id=fixture_id) -def get_fixture_name(obj): +def get_fixture_name(obj) -> str: '''Get unique fixture name''' name = getattr(obj, '__tobiko_fixture_name__', None) if name is None: @@ -85,7 +86,7 @@ def get_fixture_name(obj): return name -def get_fixture_class(obj): +def get_fixture_class(obj) -> typing.Type[fixtures.Fixture]: '''Get fixture class''' if isinstance(obj, str): obj = tobiko.load_object(obj) @@ -102,13 +103,15 @@ def get_fixture_dir(obj): return os.path.dirname(inspect.getfile(get_fixture_class(obj))) -def remove_fixture(obj, fixture_id=None, manager=None): +def remove_fixture(obj, fixture_id=None, manager=None) \ + -> typing.Optional[fixtures.Fixture]: '''Unregister fixture identified by given :param obj: if any''' manager = manager or FIXTURES return manager.remove_fixture(obj, fixture_id=fixture_id) -def setup_fixture(obj, fixture_id=None, manager=None, alternative=None): +def setup_fixture(obj, fixture_id=None, manager=None, alternative=None) \ + -> fixtures.Fixture: '''Get registered fixture and setup it up''' if alternative is None: objs = [obj] @@ -142,7 +145,8 @@ def handle_setup_error(ex_type, ex_value, ex_tb): exc_info=(ex_type, ex_value, ex_tb)) -def reset_fixture(obj, fixture_id=None, manager=None): +def reset_fixture(obj, fixture_id=None, manager=None) \ + -> fixtures.Fixture: '''Get registered fixture and reset it''' fixture = get_fixture(obj, fixture_id=fixture_id, manager=manager) with _exception.handle_multiple_exceptions(): @@ -150,7 +154,8 @@ def reset_fixture(obj, fixture_id=None, manager=None): return fixture -def cleanup_fixture(obj, fixture_id=None, manager=None): +def cleanup_fixture(obj, fixture_id=None, manager=None) \ + -> fixtures.Fixture: '''Get registered fixture and clean it up''' fixture = get_fixture(obj, fixture_id=fixture_id, manager=manager) with _exception.handle_multiple_exceptions(): @@ -158,7 +163,15 @@ def cleanup_fixture(obj, fixture_id=None, manager=None): return fixture -def get_name_and_object(obj): +def use_fixture(obj, fixture_id=None, manager=None) \ + -> fixtures.Fixture: + fixture = setup_fixture(obj, fixture_id=fixture_id, manager=manager) + _testcase.add_cleanup(tobiko.cleanup_fixture, fixture) + return fixture + + +def get_name_and_object(obj: typing.Any) \ + -> typing.Tuple[str, typing.Any]: '''Get (name, obj) tuple identified by given :param obj:''' if isinstance(obj, str): return obj, tobiko.load_object(obj) @@ -206,7 +219,7 @@ def list_required_fixtures(objects): return result -def is_test_method(obj): +def is_test_method(obj) -> bool: '''Returns whenever given object is a test method''' return ((inspect.isfunction(obj) or inspect.ismethod(obj)) and obj.__name__.startswith('test_')) @@ -313,38 +326,40 @@ def get_fixture_id(obj: typing.Any) -> typing.Any: return getattr(obj, '__tobiko_fixture_id__', None) -def get_object_name(obj): +def get_object_name(obj) -> str: '''Gets a fully qualified name for given :param obj:''' if isinstance(obj, str): return obj name = getattr(obj, '__tobiko_fixture_name__', None) - if name: + if isinstance(name, str): + assert isinstance(name, str) return name + assert name is None, f"{name} is not None" if (not inspect.isfunction(obj) and not inspect.ismethod(obj) and not inspect.isclass(obj)): obj = type(obj) - module = inspect.getmodule(obj).__name__ + module = inspect.getmodule(obj) + if module is not None: + name = getattr(obj, '__qualname__', None) + if isinstance(name, str): + return module.__name__ + '.' + name - name = getattr(obj, '__qualname__', None) - if name: - return module + '.' + name - - msg = "Unable to get fixture name from object {!r}".format(obj) - raise TypeError(msg) + raise TypeError(f"Unable to get fixture name from object {obj!r}") class FixtureManager(object): def __init__(self): - self.fixtures = {} + self.fixtures: typing.Dict[str, fixtures.Fixture] = {} def get_fixture(self, obj: typing.Any, - fixture_id: typing.Any = None): + fixture_id: typing.Any = None) \ + -> fixtures.Fixture: name, obj = get_name_and_object(obj) if fixture_id: name += f'-{fixture_id}' @@ -358,12 +373,14 @@ class FixtureManager(object): @staticmethod def init_fixture(obj: typing.Any, name: str, - fixture_id: typing.Any): + fixture_id: typing.Any) \ + -> fixtures.Fixture: return init_fixture(obj=obj, name=name, fixture_id=fixture_id) - def remove_fixture(self, obj, fixture_id=None): + def remove_fixture(self, obj, fixture_id=None) \ + -> typing.Optional[fixtures.Fixture]: name = get_object_name(obj) if fixture_id: name += '-' + str(fixture_id) diff --git a/tobiko/openstack/tests/_nova.py b/tobiko/openstack/tests/_nova.py index be8b2fa70..4ea19aa4a 100644 --- a/tobiko/openstack/tests/_nova.py +++ b/tobiko/openstack/tests/_nova.py @@ -19,9 +19,6 @@ import os import typing # noqa import time -# import testtools -import testtools - import tobiko from tobiko.shell import ping from tobiko.shell import sh @@ -82,8 +79,7 @@ def test_servers_creation(stack=TestServerCreationStack, # Create all servers stacks for fixture in fixtures: - assert isinstance(test_case, testtools.TestCase) - test_case.useFixture(fixture) + tobiko.use_fixture(fixture) # Check every server ID is unique and new server_ids = {fixture.server_id for fixture in fixtures} diff --git a/tobiko/tests/unit/test_fixture.py b/tobiko/tests/unit/test_fixture.py index 87c942145..644a72974 100644 --- a/tobiko/tests/unit/test_fixture.py +++ b/tobiko/tests/unit/test_fixture.py @@ -15,6 +15,7 @@ from __future__ import absolute_import import os import sys +import unittest import fixtures import mock @@ -180,6 +181,53 @@ class SetupFixtureTest(unit.TobikoUnitTest): result.cleanup_fixture.assert_not_called() +class UseFixtureTest(unit.TobikoUnitTest): + + def test_with_name(self): + self._test_use_fixture(canonical_name(MyFixture)) + + def test_with_type(self): + self._test_use_fixture(MyFixture) + + def test_with_instance(self): + self._test_use_fixture(MyFixture2()) + + def test_with_name_and_fixture_id(self): + self._test_use_fixture(canonical_name(MyFixture), fixture_id=5) + + def test_with_type_and_fixture_id(self): + self._test_use_fixture(MyFixture, fixture_id=6) + + def test_with_instance_and_fixture_id(self): + self._test_use_fixture(MyFixture2(), fixture_id=7) + + def _test_use_fixture(self, obj, fixture_id=None): + + fixture: MyFixture = tobiko.get_fixture( + obj=obj, fixture_id=fixture_id) # type: ignore + + class InnerTest(unittest.TestCase): + def runTest(self): + fixture.setup_fixture.assert_not_called() + fixture.cleanup_fixture.assert_not_called() + result = tobiko.use_fixture(obj, fixture_id=fixture_id) + fixture.setup_fixture.assert_called_once_with() + fixture.cleanup_fixture.assert_not_called() + self.assertIs(fixture, result) + + fixture.setup_fixture.assert_not_called() + fixture.cleanup_fixture.assert_not_called() + + result = tobiko.run_test(InnerTest()) + + fixture.setup_fixture.assert_called_once_with() + fixture.cleanup_fixture.assert_called_once_with() + + self.assertEqual(1, result.testsRun) + self.assertEqual([], result.errors) + self.assertEqual([], result.failures) + + class ResetFixtureTest(unit.TobikoUnitTest): def test_with_name(self):