Неполадка юнит-тестирования "Обязательная оболочка, функциональное ядро" в Python - PullRequest
1 голос
/ 16 мая 2019

Я создаю класс, который наследуется от NamedTuple (в частности, для неизменности), который содержит три фрагмента данных в кортеже. Я переопределяю __repr__(), чтобы обеспечить симпатичное, узнаваемое человеком представление о классе. Тем не менее, пользовательский __repr__ должен сделать несколько дорогих вычислений с данными в кортеже, чтобы обеспечить красивое представление. Таким образом, моя __repr__ функция организована следующим образом:

class MyClass(NamedTuple):
   data1: float
   data2: MyOtherClass
   data3: float


def __repr__():
    exp_1 = self.expensive_function1(self.data1, self.data2, self.data3)
    exp_2 = self.expensive_function2(self.data1, self.data2, self.data3)

    repr_component_1 = MyClass.static_func1(self.data1, exp_1, exp_2)
    repr_component_2 = MyClass.static_func2(self.data2, exp_1, exp_2)
    repr_component_3 = MyClass.static_func2(self.data3, exp_1, exp_2)

    return f"{repr_component_1} {repr_component_2}{repr_component_3}"

Нет проблем с самим кодом. Отлично работает и отлично работает. У меня проблема в том, как эффективно модульный тест статические методы repr_component_1, repr_component_2 и repr_component_3.

Если бы MyClass специально не требовалось быть неизменным, я мог бы бросить exp_1 и exp_2 в атрибуты частного класса и не иметь проблем. Однако MyClass должен быть неизменным по разным причинам.

Проблема в том, что exp_1 и exp_2 являются специфичными для экземпляра результатами, которые затем вводятся в статические методы в качестве входных данных.

Таким образом, единственный способ проверить это (с pytest) заключается в следующем:

def test_repr_component_1():
    a = MyClass(data1, data2, data3)
    exp_1 = a.expensive_function1(self.data1, self.data2, self.data3)
    exp_2 = b.expensive_function2(self.data1, self.data2, self.data3)
    result1 = MyClass.static_func1(a.data1, exp_1, exp_2)
    assert result1 == "expected result"

Тем не менее, это много строк в тестовом сценарии (мой настоящий код содержит более пяти строк)! Конечно, я хочу протестировать многие сценарии. Я уже слышал о настройках и разборках, но не знаю, применимо ли это в этой ситуации.

Хотелось бы подумать о "профессиональном" и питонском способе написания этих модульных тестов.

РЕДАКТИРОВАТЬ: Мне пришло в голову использовать Python 3.7 dataclass (из-за его способности быть «замороженным»), который позволил бы мне установить exp_1 и exp_2 в качестве частного атрибутов на этапе __post_init__, но я планирую сделать эту библиотеку общедоступной, и мне не хотелось полагаться на пользователей, имеющих только Python> = 3.7, чтобы она работала.

1 Ответ

1 голос
/ 16 мая 2019

Параметризация поможет вам уменьшить количество строк кода при написании многих сценариев:

@pytest.mark.parametrize(
    ['data1', 'data2', 'data3', 'data_argument', 'static_func', 'expected_result'],
    [
        [ # scenario 1
            data1,
            data2,
            data3,
            data1, 
            MyClass.static_func1,
            'repr_component1'
        ],
        [ # scenario 2
            data1,
            data2,
            data3,
            data2, 
            MyClass.static_func2,
            'repr_component2'
        ]
    ]
)
def test_repr_component(data1, data2, data3, data_argument, static_func, expected_result):
    a = MyClass(data1, data2, data3) # two scenarios use the same code for tests
    exp_1 = a.expensive_function1(data1, data2, data3)
    exp_2 = a.expensive_function2(data1, data2, data3)
    result = static_func1(data_argument, exp_1, exp_2)
    assert result == expected_result
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...