Унаследовать юнит-тест от родительского класса - PullRequest
0 голосов
/ 07 января 2019

Я хотел бы написать какой-нибудь тест таким образом, чтобы он выполнялся для всех классов, наследуемых от Parent.

Например, у меня есть мотор класса с двумя специализациями:

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 голосов
/ 07 января 2019

Ну, здесь есть два момента: сначала список 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 - и вы даже можете сделать это в отдельном модуле, следуя принципу открытия / закрытия .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...