# -*- coding: utf-8 -*-
"""Generic test cases."""
import unittest
import warnings
from textwrap import dedent
from typing import (
Any,
ClassVar,
Collection,
Generic,
Iterable,
Mapping,
MutableMapping,
Optional,
Type,
TypeVar,
)
__all__ = [
"GenericTestCase",
"MetaTestCase",
"TestsTestCase",
]
T = TypeVar("T")
X = TypeVar("X")
[docs]class GenericTestCase(Generic[T], unittest.TestCase):
"""Generic tests."""
cls: ClassVar[Type[T]] # type:ignore
kwargs: ClassVar[Optional[Mapping[str, Any]]] = None
instance: T
[docs] def setUp(self) -> None:
"""Set up the generic testing method."""
if not hasattr(self, "cls"):
self.skipTest(
dedent(
f"""\
The class variable `cls` was not set on {self.__class__}.
If you have implemented a subclass of :class:`unittest_template.GenericTestCase`,
make sure you do it by only importing :mod:`unittest_template`, then accessing it
with the dot operator. Do NOT do ``from unittest_template import GenericTestCase``,
otherwise your testing harness might collect it as a stand-alone test and try to
run it, which will always result in this failure.
"""
)
)
self.pre_setup_hook()
kwargs = self.kwargs or {}
self.instance_kwargs = self._pre_instantiation_hook(kwargs=dict(kwargs))
self.instance = self.cls(**self.instance_kwargs) # type: ignore
self.post_instantiation_hook()
[docs] def pre_setup_hook(self) -> None:
"""Run before setUp."""
def _pre_instantiation_hook(self, kwargs: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
"""Perform actions before instantiation, potentially modyfing kwargs."""
return kwargs
[docs] def post_instantiation_hook(self) -> None:
"""Perform actions after instantiation."""
[docs] def test_instance(self):
"""Trivially check the instance matches the class."""
self.assertIsInstance(self.instance, self.cls)
def get_subclasses(cls: Type[X]) -> Iterable[Type[X]]:
"""Get all subclasses.
:param cls: The ancestor class
:yields: Descendant classes of the ancestor class
"""
for subclass in cls.__subclasses__():
yield from get_subclasses(subclass)
yield subclass
[docs]class TestsTestCase(MetaTestCase):
"""A backwards compatible wrapper of MetaTestCase."""
[docs] def setUp(self):
"""Set up the test case."""
warnings.warn(
"unittest_templates.TestsTestCase has been renamed to unittest_tempaltes.MetaTestCase",
DeprecationWarning,
)
super().setUp()