mock / patch os.path.exists с несколькими возвращаемыми значениями

Я пытаюсь протестировать созданную мной функцию, которая выполняет итерацию по списку и вызывает os.path.exists для каждого элемента в списке. Мой тест передает функции список из 2 объектов. Мне нужно os.path.exists, чтобы вернуть True для одного из них и False для другого. Я пробовал это:

import mock
import os
import unittest

class TestClass(unittest.TestCase):
    values = {1 : True, 2 : False}
    def side_effect(arg):
        return values[arg]

    def testFunction(self):
        with mock.patch('os.path.exists') as m:
            m.return_value = side_effect # 1
            m.side_effect = side_effect # 2

            arglist = [1, 2]
            ret = test(argList)

Использование любой, но не обеих строк №1 и №2 дает NameError: global name 'side_effect' is not defined

Я нашел этот вопрос и изменил свой код следующим образом:

import mock
import os

class TestClass(unittest.TestCase):
    values = {1 : True, 2 : False}
    def side_effect(arg):
        return values[arg]

    def testFunction(self):
        mockobj = mock(spec=os.path.exists)
        mockobj.side_effect = side_effect

        arglist = [1, 2]
        ret = test(argList)

И это дает TypeError: 'module' object is not callable. Я также пробовал переключать эти строки:

mockobj = mock(spec=os.path.exists)
mockobj.side_effect = side_effect

для этого

mockobj = mock(spec=os.path)
mockobj.exists.side_effect = side_effect

и это

mockobj = mock(spec=os)
mockobj.path.exists.side_effect = side_effect

с той же ошибкой. Может ли кто-нибудь указать, что я делаю неправильно и что я могу сделать, чтобы это работало?

РЕДАКТИРОВАТЬ: опубликовав свой ответ ниже, я понял, что мой первый фрагмент кода действительно работает, мне просто нужно m.side_effect = TestClass.side_effect вместо m.side_effect = side_effect.


person Yep_It's_Me    schedule 21.02.2014    source источник
comment
Лучше изолировать проблемы и создать минимальные рабочие примеры (MWE). Изучите области на Python (я предоставляю ссылку, docs.python .org / 3 / reference /). Это очень простая вещь в Python, прошу прощения за отрицательные отзывы.   -  person Yaroslav Nikitenko    schedule 16.01.2021
comment
Предоставленный TestClass не является MWE?   -  person Yep_It's_Me    schedule 19.01.2021
comment
Возможно, вы правы, что для unittest этот класс является MWE. Я использую pytest, и для этого достаточно одной функции. Однако я называю это не MWE, потому что есть два класса, а не один. В первом примере ошибка не имеет ничего общего с mock. Во втором тоже, потому что если вы запустите mock(spec=os.path.exists) (после импорта mock) в интерпретаторе, это не сработает с той же ошибкой.   -  person Yaroslav Nikitenko    schedule 19.01.2021
comment
Обратите внимание на то, что тест / макет на самом деле не имеет ничего общего с ошибкой. Однако в то время я этого не знал, отсюда и вопрос SO. Кроме того, два класса, которые вы видите, на самом деле являются одним и тем же классом, только с парой модификаций, чтобы показать, почему я думаю, что os.path.exists ведет себя по-разному. Если бы я показал только одно из них, это не было бы MWE, потому что это не продемонстрировало бы os.path.exists другого поведения.   -  person Yep_It's_Me    schedule 20.01.2021
comment
В любом случае, спасибо за отзыв и за то, что оставили комментарий, объясняющий отрицательный голос.   -  person Yep_It's_Me    schedule 20.01.2021
comment
Добро пожаловать. Я думаю, что os.path.exists ведет себя по-другому - я не вижу, как он может вести себя по-другому при возникновении ошибки. Вопрос не в нюансах сообщения об ошибках. Как я уже писал, первый пример показывает отсутствие базовых знаний о Python. Если вас действительно волнует отрицательный голос, пожалуйста, просто удалите его (чтобы здесь было меньше беспорядка), и я удалю отрицательный голос. Я поддержал ваш ответ.   -  person Yaroslav Nikitenko    schedule 20.01.2021


Ответы (2)


Итак, после небольшого количества исследований и проб и ошибок, большинство примеров приведены здесь: http://www.voidspace.org.uk/python/mock/patch.html, я решил свою проблему.

import mock
import os

def side_effect(arg):
    if arg == 1:
        return True
    else:
        return False

class TestClass(unittest.TestCase):
    patcher = mock.patch('os.path.exists')
    mock_thing = patcher.start()
    mock_thing.side_effect = side_effect
    arg_list = [1, 2]
    ret = test(arg_list)
    self.assertItemsEqual([1], ret)

test вызывает os.path.exist для каждого элемента в arg_list и возвращает список всех элементов, для которых os.path.exist вернул True. Теперь этот тест проходит так, как я хочу.

person Yep_It's_Me    schedule 24.02.2014
comment
А что, если функция имеет один и тот же аргумент при обоих вызовах? - person hithwen; 21.03.2014
comment
Что ты имеешь в виду? Если os.path.exists передается 1 для обоих вызовов? Затем он вернет True, потому что это то, что говорит побочный эффект. Но ему не будет передан один и тот же аргумент дважды, потому что функция test выполняет итерацию через arg_list и вызывает os.path.exists для каждого элемента в списке. Итак, пока оба элемента в списке не 1, он будет работать должным образом. - person Yep_It's_Me; 23.03.2014
comment
Я имел в виду, если я хочу вызвать os.path.exists два раза с одним и тем же аргументом и вернуть сначала False, а затем True. Я понял, как это сделать, определив функцию, которая будет подсчитывать, сколько раз было вызвано, возвращать разные значения и использовать ее как side_effect. - person hithwen; 24.03.2014

Вы могли бы сделать self.side_effect, я верю. поскольку первоначальное определение не было глобальным, вызов side_effect просматривает глобальную область видимости

person kio    schedule 15.09.2016