Подтвердить что ты не робот

Mocking функции с использованием python mock

Я пытаюсь Mock функция (которая возвращает некоторый внешний контент), используя модуль python mock (http://www.voidspace.org.uk/python/mock/index.html).

У меня возникают некоторые проблемы с издевательскими функциями, которые импортируются в модуль.

Например, в util.py у меня есть

def get_content():
  return "stuff"

Я хочу mock util.get_content, чтобы он возвращал что-то еще.

Я пытаюсь это сделать:

util.get_content=Mock(return_value="mocked stuff")

Если get_content вызывается внутри другого модуля, на самом деле он действительно не возвращает возвращенный объект. Я что-то пропустил с точки зрения использования Mock?

Обратите внимание, что если я вызываю следующее, все будет работать правильно:

>>> util.get_content=Mock(return_value="mocked stuff")
>>> util.get_content()
"mocked stuff"

Однако, если get_content вызывается из другого модуля, он вызывает исходную функцию вместо измененной версии:

>>> from mymodule import MyObj
>>> util.get_content=Mock(return_value="mocked stuff")
>>> m=MyObj()
>>> m.func()
"stuff"

Содержимое mymodule.py

from util import get_content

class MyObj:    
    def func():
        get_content()

Итак, я догадываюсь, что мой вопрос: как я могу вызвать функцию Mocked из функции внутри модуля, который я вызываю?

Похоже, здесь может быть виноват from module import function, поскольку он не указывает на функцию Mocked.

4b9b3361

Ответ 1

Я думаю, что у меня есть обходное решение, хотя я все еще не совсем понимаю, как решить общий случай

В mymodule, если я заменю

from util import get_content

class MyObj:    
    def func():
        get_content()

с

import util

class MyObj:    
    def func():
        util.get_content()

Кажется, что Mock вызывается. Похоже, что пространства имен должны совпадать (что имеет смысл). Однако, странно, что я ожидал бы

import mymodule
mymodule.get_content = mock.Mock(return_value="mocked stuff")

сделать трюк в исходном случае, когда я использую синтаксис from/import (который теперь тянет get_content в mymodule). Но это все еще относится к unmocked get_content.

Оказывает значение пространства имен - просто нужно помнить об этом при написании кода.

Ответ 2

Вам нужно исправить функцию, в которой она используется. В вашем случае это будет в модуле mymodule.

import mymodule
>>> mymodule.get_content = Mock(return_value="mocked stuff")
>>> m = mymodule.MyObj()
>>> m.func()
"mocked stuff"

В документах есть ссылка: http://docs.python.org/dev/library/unittest.mock.html#where-to-patch

Ответ 3

Предположим, вы создаете свой макет внутри модуля foobar:

import util, mock
util.get_content = mock.Mock(return_value="mocked stuff")

Если вы импортируете mymodule и вызовите util.get_content без предварительного импорта foobar, ваш макет не будет установлен:

import util
def func()
    print util.get_content()
func()
"stuff"

Вместо

import util
import foobar   # substitutes the mock
def func():
    print util.get_content()
func()
"mocked stuff"

Обратите внимание, что foobar можно импортировать из любого места (модуль A импортирует B, который импортирует foobar), пока foobar оценивается до вызова util.get_content.

Ответ 4

Общий случай - использовать patch из mock. Рассмотрим следующее:

utils.py

def get_content():
    return 'stuff'

mymodule.py

from util import get_content


class MyClass(object):

    def func(self):
        return get_content()

test.py

import unittest

from mock import patch

from mymodule import MyClass

class Test(unittest.TestCase):

    @patch('mymodule.get_content')
    def test_func(self, get_content_mock):
        get_content_mock.return_value = 'mocked stuff'

        my_class = MyClass()
        self.assertEqual(my_class.func(), 'mocked stuff')
        self.assertEqual(get_content_mock.call_count, 1)
        get_content_mock.assert_called_once()

Обратите внимание, что get_content высмеивается, это не util.get_content, а mymodule.get_content, поскольку мы используем его в mymodule.

Выше было протестировано с mock v2.0.0, nosetests v1.3.7 и python v2.7.9.

Ответ 5

Пока он не дает ответ на ваш вопрос напрямую, другой возможной альтернативой является преобразование вашей функции в статический метод с помощью метода @static.

Итак, вы можете преобразовать свои утилиты модулей в класс, используя что-то вроде:

class util(object):
     @staticmethod
     def get_content():
         return "stuff"

Затем макет исправляет его правильно.