Ну, здесь есть два момента: сначала список Motor
дочерних классов, затем экземпляр каждого из этих классов.
Глупое простое решение - поддерживать эти списки в setUp
:
вашего тестового набора.
from motors import ElectricMotor, DieselMotor
class MotorTest(unittest.TestCase):
_MOTOR_CHILD_CLASSES = [ElectricMotor, DieselMotor]
def setUp(self):
self.motor_child_instances = [cls() for cls in self._MOTOR_CHILD_CLASSES]
def test_energy_should_be_conserved():
for class_instance in self.motor_child_instances:
self.assertEqual(sum(class_instance.run(10)), 10)
# etc
Если ваши Motor
подклассы __init__()
ожидают других аргументов (чего не должно быть, если вы хотите правильное подтипирование в соответствии с принципом подстановки Лискова - но хорошо, "практичность превосходит чистоту"), Вы можете добавить эти аргументы в свой список MOTOR_CHILD_CLASSES
:
# (cls, args, kw) tuples
_MOTOR_CHILD_CLASSES = [
(ElectricMotor, (42,), {"battery":"ioncad"}),
(DieselMotor, (), {"cylinders":6}),
]
и используйте их в setUp()
:
self.motor_child_instances = [
cls(*args, **kw) for cls, args, kw in self._MOTOR_CHILD_CLASSES
]
Для чего-то более «автоматизированного», вы можете использовать пользовательский метакласс на Motor
, чтобы он мог регистрировать свои подклассы и предоставлять их список, но тогда вы потеряете возможность предоставлять аргументы для каждого класса - и вы Вы также сделаете ваш тестовый код гораздо менее читабельным и предсказуемым.
Теперь другой - и IMHO намного лучший - подход заключается в использовании вместо этого наследования в ваших тестах: определите объект mixin со всеми тестами, общими для всех Motor
дочерних классов:
class MotorTestMixin(object):
# must be combined with a unittest.TestCase that
# defines `self.instance` as a `Motor` subclass instance
def test_energy_should_be_conserved(self):
self.assertEqual(sum(self.instance.run(10)), 10)
def test_should_produce_heat(self):
heat, motion = self.instance.run(10)
self.assertGreater(heat, 0)
тогда есть один TestCase для каждого подкласса:
class DieselMotorTest(MotorTestMixin, TestCase):
def setUp(self):
self.instance = DieselMotor()
class ElectricMotorTest(MotorTestMixin, TestCase):
def setUp(self):
self.instance = ElectricMotor()
Одним из преимуществ этого подхода (другими являются простота, удобочитаемость и намного лучшая отчетность об ошибках при неудачных тестах - вы сразу узнаете, какой подкласс потерпел неудачу, не выполняя ничего особенного), что у вас нет прикоснуться к существующему коду при добавлении нового подкласса Motor
- вам просто нужно добавить для него новый отдельный TestCase - и вы даже можете сделать это в отдельном модуле, следуя принципу открытия / закрытия .