Условное издевательство: вызвать исходную функцию, если условие соответствует

Как я могу условно вызвать оригинальный метод в макете?

В этом примере я хочу только подделать возвращаемое значение, если bar=='x'. В противном случае я хочу вызвать оригинальный метод.

def mocked_some_method(bar):
    if bar=='x':
        return 'fake'
    return some_how_call_original_method(bar)

with mock.patch('mylib.foo.some_method', mocked_some_method):
    do_some_stuff()

Я знаю, что это немного странно. Если я хочу подделать mylib.foo.some_method в сторону do_some_stuff(), это должно быть без условий. Все (не некоторые) звонки на some_method должны быть осмеяны.

В моем случае это интеграционный тест, а не крошечный юнит-тест, а mylib.foo.some_method является своего рода диспетчером, который очень часто используется. И в одном случае мне нужно подделать результат.

Обновление

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

Ответ 1

Если вам нужно просто поменять поведение, не заботясь о функции moker calls assert, вы можете использовать аргумент new; в противном случае вы можете использовать side_effect, которые могут быть вызваны.

Я предполагаю, что some_method - это метод объекта (вместо staticmethod), поэтому вам нужна ссылка на его объект, чтобы вызвать его. Ваша оболочка должна объявить в качестве первого аргумента объект и ваш патч использовать autospec=True для использования правильной сигнатуры для случая side_effect.

Заключительный трюк - это сохранить исходную ссылку на метод и использовать его для совершения вызова.

orig = mylib.foo.some_method
def mocked_some_method(self, bar):
    if bar=='x':
        return 'fake'
    return orig(self, bar)

#Just replace:
with mock.patch('mylib.foo.some_method', new=mocked_some_method):
    do_some_stuff()

#Replace by mock
with mock.patch('mylib.foo.some_method', side_effect=mocked_some_method, autospec=True) as mock_some_method:
    do_some_stuff()
    assert mock_some_method.called