Source code for slash.core.test

import functools
import itertools
from types import GeneratorType

from ..exception_handling import handling_exceptions
from ..exceptions import SkipTest, InvalidTest
from .fixtures.parameters import bound_parametrizations_context
from .runnable_test import RunnableTest
from .runnable_test_factory import RunnableTestFactory
from .requirements import get_requirements
from .tagging import get_tags
from .fixtures.utils import nofixtures

class TestTestFactory(RunnableTestFactory):

    def __init__(self, testclass):
        super(TestTestFactory, self).__init__(testclass)
        self.testclass = testclass

    def get_class_name(self):
        return self.testclass.__name__

    def _generate_tests(self, fixture_store):
        if is_abstract_base_class(self.testclass):
            return

        for test_method_name in dir(self.testclass):
            if not test_method_name.startswith("test"):
                continue

            for fixture_variation in self._iter_parametrization_variations(test_method_name, fixture_store):
                case = self.testclass(
                    test_method_name,
                    fixture_store=fixture_store,
                    fixture_namespace=fixture_store.get_current_namespace(),
                    variation=fixture_variation,
                )
                if self.testclass.__slash_skipped__:
                    case.run = functools.partial(SkipTest.throw, self.testclass.__slash_skipped_reason__)
                yield case  # pylint: disable=protected-access

    def _iter_parametrization_variations(self, test_method_name, fixture_store):
        return fixture_store.iter_parametrization_variations(methods=itertools.chain(
            zip(itertools.repeat('before'), self._get_all_before_methods()),
            zip(itertools.repeat('after'), self._get_all_after_methods()),
            [getattr(self.testclass, test_method_name)],
        ))

    def _get_all_before_methods(self):
        return self._iter_inherited_methods('before')

    def _get_all_after_methods(self):
        return self._iter_inherited_methods('after')

    def _iter_inherited_methods(self, name):

        for cls in self.testclass.__mro__:
            if hasattr(cls, name):
                yield getattr(cls, name)

    def get_unmet_requirements(self):
        raise NotImplementedError() # pragma: no cover


[docs]class Test(RunnableTest): """ This is a base class for implementing unittest-style test classes. """ def __init__(self, test_method_name, fixture_store, fixture_namespace, variation): super(Test, self).__init__(fixture_store, fixture_namespace, variation) self._test_method_name = test_method_name def get_test_function(self): return getattr(self, self._test_method_name) def get_tags(self): test_tags = (get_tags(type(self)) + get_tags(getattr(type(self), self._test_method_name)) + self.get_variation().tags) if nofixtures.is_marked(self.get_test_function()): return test_tags return test_tags + self._get_fixture_tags() __slash_skipped__ = False __slash_skipped_reason__ = None __slash_needed_contexts__ = None @classmethod def skip_all(cls, reason=None): cls.__slash_skipped__ = True cls.__slash_skipped_reason__ = reason def get_required_fixture_objects(self): method = self.get_test_function() return self._fixture_store.get_required_fixture_objects(method, namespace=self._fixture_namespace) def get_address_in_factory(self): returned = '' if self._test_method_name is not None: returned += ".{}".format(self._test_method_name) return returned def _get_call_string(self, kwargs): if not kwargs: return "" return "({})".format(", ".join("{}={!r}".format(k, v) for k, v in kwargs.items())) def get_requirements(self): test_requirements = get_requirements(type(self)) + get_requirements(self.get_test_function()) if nofixtures.is_marked(self.get_test_function()): return test_requirements return list(set(test_requirements + self._get_fixtures_requirements()))
[docs] def run(self): # pylint: disable=E0202 """ .. warning:: Not to be overriden """ method = self.get_test_function() with bound_parametrizations_context(self._variation, self._fixture_store, self._fixture_namespace): _call_with_fixtures = functools.partial(self._fixture_store.call_with_fixtures, namespace=self._fixture_namespace) _call_with_fixtures(self.before) try: with handling_exceptions(): result = _call_with_fixtures(method, trigger_test_start=True) if isinstance(result, GeneratorType): raise InvalidTest('{} is a generator. Running generators is not supported'.format(method)) finally: with handling_exceptions(): _call_with_fixtures(self.after, trigger_test_end=True)
[docs] def before(self): """ Gets called before each separate case generated from this test class """ pass
[docs] def after(self): """ Gets called after each separate case from this test class executed, assuming :meth:`.before` was successful. """ pass
def _format_kwargs(self, kwargs): return ", ".join("{}={!r}".format(x, y) for x, y in kwargs.items())
[docs]def abstract_test_class(cls): """ Marks a class as **abstract**, thus meaning it is not to be run directly, but rather via a subclass. """ assert issubclass(cls, Test), "abstract_test_class only operates on slash.Test subclasses" cls.__slash_abstract__ = True return cls
def is_abstract_base_class(cls): """ Checks if a given class is abstract. .. seealso:: :func:`abstract_test_class` """ return bool(cls.__dict__.get("__slash_abstract__", False)) def is_valid_test_name(name): return name.startswith('test_')