""" Interface for customizing the behavior of a test fixture. """ from __future__ import absolute_import import sys from ..testcases import interface as testcase from ... import errors from ...logging import loggers from ...utils import registry _HOOKS = {} def make_hook(class_name, *args, **kwargs): """ Factory function for creating Hook instances. """ if class_name not in _HOOKS: raise ValueError("Unknown hook class '%s'" % class_name) return _HOOKS[class_name](*args, **kwargs) class Hook(object): """ The common interface all Hooks will inherit from. """ __metaclass__ = registry.make_registry_metaclass(_HOOKS) REGISTERED_NAME = registry.LEAVE_UNREGISTERED def __init__(self, hook_logger, fixture, description): """ Initializes the Hook with the specified fixture. """ if not isinstance(hook_logger, loggers.HookLogger): raise TypeError("logger must be a HookLogger instance") self.logger = hook_logger self.fixture = fixture self.description = description def before_suite(self, test_report): """ The test runner calls this exactly once before they start running the suite. """ pass def after_suite(self, test_report): """ The test runner calls this exactly once after all tests have finished executing. Be sure to reset the behavior back to its original state so that it can be run again. """ pass def before_test(self, test, test_report): """ Each test will call this before it executes. """ pass def after_test(self, test, test_report): """ Each test will call this after it executes. """ pass class DynamicTestCase(testcase.TestCase): # pylint: disable=abstract-method def __init__(self, logger, test_name, description, base_test_name, hook): testcase.TestCase.__init__(self, logger, "Hook", test_name) self.description = description self._hook = hook self._base_test_name = base_test_name def run_dynamic_test(self, test_report): """Helper method to run a dynamic test and update the test report.""" test_report.startTest(self, dynamic=True) try: self.run_test() except (errors.TestFailure, self.failureException) as err: self.return_code = 1 self.logger.exception("{0} failed".format(self.description)) test_report.addFailure(self, sys.exc_info()) raise errors.TestFailure(err) except: self.return_code = 2 test_report.addFailure(self, sys.exc_info()) raise else: self.return_code = 0 test_report.addSuccess(self) finally: test_report.stopTest(self) def as_command(self): return "(dynamic test case)" @classmethod def create_before_test(cls, logger, base_test, hook, *args, **kwargs): """Creates a hook dynamic test to be run before an existing test.""" base_test_name = base_test.short_name() test_name = cls._make_test_name(base_test_name, hook) description = "{} before running '{}'".format(hook.description, base_test_name) return cls(logger, test_name, description, base_test_name, hook, *args, **kwargs) @classmethod def create_after_test(cls, logger, base_test, hook, *args, **kwargs): """Creates a hook dynamic test to be run after an existing test.""" base_test_name = base_test.short_name() test_name = cls._make_test_name(base_test_name, hook) description = "{} after running '{}'".format(hook.description, base_test_name) return cls(logger, test_name, description, base_test_name, hook, *args, **kwargs) @staticmethod def _make_test_name(base_test_name, hook): return "{}:{}".format(base_test_name, hook.__class__.__name__)