макет python экземпляр метода с аргументом или вызовом оригинала - PullRequest
0 голосов
/ 05 марта 2020

Для любого класса с методом экземпляра foo, который зависит от переменной экземпляра, например,


class A:
    def __init__(self, x: int):
        self.x = x

    def foo(self, y: int):
        return self.x * y


def test_foo(mocker):
    a = A(1)
    y = -10
    assert a.foo(y) < 0
    mocker.patch.object(a, "foo", return_value=a.x * abs(y))
    assert a.foo(y) > 0

. Вопрос в том, как имитировать A(1).foo(y) только для интересующего тестового значения, например, y < 0, в противном случае верните исходный вызов A(1).foo(y)? В приведенном выше примере значение y уже известно, но для этого вопроса, пожалуйста, предположим, что оно неизвестно (скажем, в стороннем коде), и его необходимо проверить по вызову foo(). Кажется, что python mocks применяются ко всем вызовам к A(1).foo(y) без каких-либо опций, чтобы применять mock только для указанных c значений y, проверяя аргументы вызова метода.

Например, используя pytest-mock, приведенный выше тест работает, но у него нет опций для проверки вызова метода, проверки аргументов для вызова метода и внедрения макета только в том случае, если аргументы соответствуют некоторым интересующим значениям и в противном случае вызывают оригинальный метод.

Является ли решение для создания подкласса A, который может проверять аргументы и возвращать фиктивное значение или вызывать super, а затем использовать mock для внедрения этого подкласса вместо A? Например, основываясь на приведенном выше примере кода:


class B(A):

    def foo(self, y: int):
        if y < 0:
            return self.x * abs(y)
        else:
            super().foo(y)


def test_b_foo(mocker):
    a = A(1)
    y = -10
    assert a.foo(y) < 0
    mocker.patch.object(A, "foo", side_effect=B(a.x).foo)
    assert a.foo(y) > 0

Это работает нормально, но предполагает, что A уже инициализирован, и использует экземпляр a для инициализации B. Как может фиктивная замена звонков на A(i) звонками на B(i), чтобы тест на самом деле звонил на B(i).foo(y)? Что происходит, когда макеты для указанного c аргумента A(i).foo(y) для какого-либо указанного c y должны применяться к методу A.foo независимо от того, как или когда он инициализируется? Я ищу что-то вроде ruby mocks, которые применяют возвращаемое значение mock только при вызове исправленного объекта (метода) с некоторыми заданными c аргументами. Кажется, что какой-то шпион может динамически вводить возвращаемое значение, когда обнаруживает вызов метода с указанными c аргументами (для короткого замыкания исходного метода), но в противном случае вызывает оригинальный метод. Я думаю, это будет что-то вроде:

m = mock.patch.object(A, "foo")
m.called_with(y < 0).return_value = mock_specific_to_args
# otherwise m.foo(10) calls the original method

1 Ответ

0 голосов
/ 05 марта 2020

Проверьте значение y, чтобы решить, стоит ли имитировать A.foo или нет.

def test_foo(mocker):
    a = A(1)
    y = -10
    assert a.foo(y) < 0
    if y < 0:
        mocker.patch.object(a, "foo", return_value=a.x * abs(y))
    assert a.foo(y) > 0
...