【问题标题】:Inherit unit test from parent class从父类继承单元测试
【发布时间】:2019-01-07 11:10:16
【问题描述】:

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

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

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:

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

有什么想法吗? 谢谢 里卡多

【问题讨论】:

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


    【解决方案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 子类共有的所有测试:

    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 之后的不同模块中这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-07
      • 1970-01-01
      • 2019-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多