从父类继承单元测试

时间:2019-01-07 11:10:16

标签: python python-3.x unit-testing testing multiple-inheritance

我想编写一些测试,并对从父类继承的所有类执行测试。

例如,我有两个专业的班级电动机:

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:

或其他编程模式以获得相同的结果。

有什么主意吗? 谢谢 里卡多

1 个答案:

答案 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>