我想编写一些测试,并对从父类继承的所有类执行测试。
例如,我有两个专业的班级电动机:
class Motor():
def run(self, energy):
pass
class ElectricMotor(Motor):
def run(self, electric_energy):
heat = electric_energy * 0.99
motion = electric_energy * 0.01
return heat, motion
class DieselMotor(Motor):
def run(self, diesel_energy):
heat = diesel_energy * 0.65
motion = diesel_energy * 0.35
return heat, motion
然后我有两项适用于每种电动机的测试:
class MotorTest(unittest.TestCase):
def test_energy_should_be_conserved():
for class_instance in all_motor_child_classes:
energy=10
assert sum(class_instance.run(energy))==energy
energy=20
assert sum(class_instance.run(energy))==energy
def test_motors_should_produce_heat():
for class_instance in all_motor_child_classes:
energy = 10
heat, motion=class_instance.run(energy)
assert heat>0
我正在寻找一种执行循环的方法
for class_instance in all_motor_child_classes:
或其他编程模式以获得相同的结果。
有什么主意吗? 谢谢 里卡多
答案 0 :(得分:1)
好,这里有两点:首先有一个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__()
期望使用不同的参数(如果您想拥有proper subtyping according to liskov substitution principle,则不应该使用它们,但是,“实用性胜过纯度”),可以添加这些参数您的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
上使用自定义元类,以便它可以注册其子类并提供它们的列表,但是随后您将失去提供按类参数的功能-并且还将使测试代码的可读性和可预测性大大降低。
现在,另一种方法(恕我直言,好得多更好)是在测试中使用继承:定义一个mixin对象,其中包含所有Motor
子类都通用的所有测试:< / p>
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-甚至可以在open/closed principle之后的不同模块中这样做。 / p>