From 487281e7a73ff354555a9d95614ab4c6e0c0df40 Mon Sep 17 00:00:00 2001 From: Federico Ressi Date: Tue, 18 Dec 2018 18:02:24 +0100 Subject: [PATCH] Create abstract fixture manager Allow to decople global fixture definition from test cases. Fixtures can be defined as a subclass of tobiko.Fixture class MyFixture(tobiko.Fixture): ... Tobiko will reference to it as: '.MyFixture' where module-name is the full name of the module where the class is defined. Fixture name are accessible via 'fixture_name' class attribute. tobiko.Fixture subclass has to implement bellow methods def create_fixture(self): # mandatory ... def delete_fixture(self): # optional method ... Test cases to create a fixture should mane one of below calls: fixture = tobiko.create_fixture() fixture = tobiko.create_fixture() Existing fixtures can be find using one of below calls: fixture = tobiko.get_fixture() fixture = tobiko.get_fixture() Existing fixtures can be deleted using one of below calls: tobiko.delete_fixture() tobiko.delete_fixture() Change-Id: I5c104a732234ab2183fbfb9909cba4a445f59b60 --- tobiko/__init__.py | 22 +++++++ tobiko/common/managers/fixture.py | 97 +++++++++++++++++++++++++++++++ tobiko/tests/test_fixture.py | 89 ++++++++++++++++++++++++++++ tox.ini | 4 +- 4 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 tobiko/common/managers/fixture.py create mode 100644 tobiko/tests/test_fixture.py diff --git a/tobiko/__init__.py b/tobiko/__init__.py index e69de29bb..cb29eb32b 100644 --- a/tobiko/__init__.py +++ b/tobiko/__init__.py @@ -0,0 +1,22 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from __future__ import absolute_import + +from tobiko.common.managers import fixture as fixture_manager + + +Fixture = fixture_manager.Fixture + +get_fixture = fixture_manager.FIXTURES.get +create_fixture = fixture_manager.FIXTURES.create +delete_fixture = fixture_manager.FIXTURES.delete diff --git a/tobiko/common/managers/fixture.py b/tobiko/common/managers/fixture.py new file mode 100644 index 000000000..e73c14deb --- /dev/null +++ b/tobiko/common/managers/fixture.py @@ -0,0 +1,97 @@ +# Copyright 2018 Red Hat +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from __future__ import absolute_import + + +import abc +import inspect + +import six + + +def get_fixture_name(obj): + if isinstance(obj, six.string_types): + return obj + + elif (isinstance(obj, six.class_types) and + issubclass(obj, Fixture)): + return obj.fixture_name + + msg = "{!r} is not a string type or a subclass of {!s}".format( + obj, Fixture) + raise TypeError(msg) + + +class FixtureManager(object): + + def __init__(self): + self.fixtures = {} + + def set(self, name, cls): + if not issubclass(cls, Fixture): + msg = "{!r} is not a subclass of {!s}".format(cls, Fixture) + raise TypeError(msg) + fixture = cls() + actual_fixture = self.fixtures.setdefault(name, fixture) + if actual_fixture is not fixture: + msg = "Fixture with named {!r} already registered: {!r}".format( + name, actual_fixture) + raise ValueError(msg) + return fixture + + def get(self, cls_or_name): + name = get_fixture_name(cls_or_name) + fixture = self.fixtures.get(name) + if fixture is None: + raise ValueError('Invalid fixture name: {!r}'.format(name)) + return fixture + + def create(self, cls_or_name): + fixture = self.get(cls_or_name) + fixture.create_fixture() + return fixture + + def delete(self, cls_or_name): + fixture = self.get(cls_or_name) + fixture.delete_fixture() + return fixture + + +FIXTURES = FixtureManager() + + +class FixtureMeta(abc.ABCMeta): + + def __new__(cls, name, bases, members): + fixture_class = super(FixtureMeta, cls).__new__(cls, name, bases, + members) + if not inspect.isabstract(fixture_class): + fixture_name = getattr(fixture_class, 'fixture_name', None) + if fixture_name is None: + fixture_class.fixture_name = fixture_name = ( + fixture_class.__module__ + '.' + + fixture_class.__name__) + FIXTURES.set(fixture_name, fixture_class) + return fixture_class + + +@six.add_metaclass(FixtureMeta) +class Fixture(object): + + @abc.abstractmethod + def create_fixture(self): + pass + + def delete_fixture(self): + pass diff --git a/tobiko/tests/test_fixture.py b/tobiko/tests/test_fixture.py new file mode 100644 index 000000000..ae73c84f9 --- /dev/null +++ b/tobiko/tests/test_fixture.py @@ -0,0 +1,89 @@ +# Copyright 2018 Red Hat +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from __future__ import absolute_import + +import tobiko +from tobiko.tests import base + + +class TestFixture(tobiko.Fixture): + + def __init__(self): + self.created = False + self.deleted = False + + reset = __init__ + + def create_fixture(self): + self.created = True + + def delete_fixture(self): + self.deleted = True + + +class FixtureTypeTest(base.TobikoTest): + + fixture_type = TestFixture + fixture_name = __name__ + '.' + TestFixture.__name__ + + @classmethod + def setUpClass(cls): + super(FixtureTypeTest, cls).setUpClass() + cls.fixture = tobiko.get_fixture(cls.fixture_name) + + def setUp(self): + super(FixtureTypeTest, self).setUp() + self.fixture.reset() + + def test_fixture_type(self): + self.assertIsInstance(self.fixture, self.fixture_type) + + def test_fixture_name(self): + self.assertEqual(self.fixture_name, self.fixture.fixture_name) + + def test_get_fixture_by_name(self): + self._test_get_fixture(self.fixture_name) + + def test_get_fixture_by_type(self): + self._test_get_fixture(self.fixture_type) + + def _test_get_fixture(self, obj): + fixture = tobiko.get_fixture(obj) + self.assertIs(self.fixture, fixture) + self.assertFalse(fixture.created) + self.assertFalse(fixture.deleted) + + def test_create_fixture_by_name(self): + self._test_create_fixture(self.fixture_name) + + def test_create_fixture_by_type(self): + self._test_create_fixture(self.fixture_type) + + def _test_create_fixture(self, obj): + fixture = tobiko.create_fixture(obj) + self.assertIs(self.fixture, fixture) + self.assertTrue(fixture.created) + self.assertFalse(fixture.deleted) + + def test_delete_fixture_by_name(self): + self._test_delete_fixture(self.fixture_name) + + def test_delete_fixture_by_type(self): + self._test_delete_fixture(self.fixture_type) + + def _test_delete_fixture(self, obj=TestFixture): + fixture = tobiko.delete_fixture(obj) + self.assertIs(self.fixture, fixture) + self.assertFalse(fixture.created) + self.assertTrue(fixture.deleted) diff --git a/tox.ini b/tox.ini index cfa2ecd65..d06caa7b9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = pep8,pylint,py35,py36,py27 +envlist = pep8,pylint,py35,py36,py37,py27 minversion = 2.0 @@ -24,7 +24,7 @@ commands = find . -type f -name ".coverage*" -delete find . -type f -name "*.pyc" -delete coverage erase - stestr --test-path ./tobiko/tests/cmd run {posargs} + stestr --test-path ./tobiko/tests run --black-regex 'scenario' {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml