首页 > 解决方案 > 从父类继承单元测试

问题描述

我想以一种对从父类继承的所有类执行的方式编写一些测试。

例如,我有两个专业的电机类:

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:

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

任何想法?谢谢里卡多

标签: pythonpython-3.xunit-testingtestingmultiple-inheritance

解决方案


嗯,这里有两点:首先有一个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__()需要不同的参数(如果您想根据 liskov 替换原则进行正确的子类型化,则它们不应该这样做- 但是,“实用性胜过纯度”),您可以将这些参数添加到您的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以便它可以注册其子类并提供它们的列表,但是您将失去提供每个类参数的能力 - 您还将进行测试代码的可读性和可预测性要低得多。

现在另一种 - 恕我直言更好Motor- 方法是在您的测试中使用继承:定义一个 mixin 对象,其中包含所有子类共有的所有测试:

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——你甚至可以在一个不同的模块中这样做,遵循开放/封闭原则


推荐阅读