Пересмешивать файл, содержащий данные JSON, с помощью mock.patch и mock_open - PullRequest
0 голосов
/ 04 марта 2019

Я пытаюсь протестировать метод, который требует использования json.load в Python 3.6.И после нескольких попыток я попытался запустить тест «нормально» (с обычным unittest.main () из CLI) и в iPlthon REPL.

Имея следующую функцию (упрощено для целей примера)

def load_metadata(name):
    with open("{}.json".format(name)) as fh:
        return json.load(fh)

со следующим тестом:

class test_loading_metadata(unittest2.TestCase):
    @patch('builtins.open', new_callable=mock_open(read_data='{"disabled":True}'))
    def test_load_metadata_with_disabled(self, filemock):
        result = load_metadata("john")
        self.assertEqual(result,{"disabled":True})
        filemock.assert_called_with("john.json")

Результат выполнения файла теста приводит к разрыву сердца:

TypeError: the JSON object must be str, bytes or bytearray, not 'MagicMock'

При выполнениито же самое в командной строке дает успешный результат.

Я пробовал несколькими способами (исправление с помощью with, как декоратор), но единственное, о чем я могу думать, это unittestсама библиотека, и что бы она ни делала, чтобы мешать mock и patch.

Также проверил версии python в virtualenv и ipython, версии библиотеки json.

Я бы хотелзнать, почему то, что выглядит как один и тот же код, работает в одном месте и не работает в другом.Или, по крайней мере, указатель в правильном направлении, чтобы понять, почему это может происходить.

1 Ответ

0 голосов
/ 04 марта 2019

json.load() просто вызывает fh.read(), но fh не является mock_open() объектом.Это mock_open()() объект, потому что new_callable называется и называется перед исправлением для создания замещающего объекта:

>>> from unittest.mock import patch, mock_open
>>> with patch('builtins.open', new_callable=mock_open(read_data='{"disabled":True}')) as filemock:
...     with open("john.json") as fh:
...         print(fh.read())
...
<MagicMock name='open()().__enter__().read()' id='4420799600'>

Не используйте new_callable, вы не хотите, чтобы вашmock_open() объект для вызова!Просто передайте его как new аргумент @patch() (это также второй позиционный аргумент, так что вы можете опустить new= здесь):

@patch('builtins.open', mock_open(read_data='{"disabled":True}'))
def test_load_metadata_with_disabled(self, filemock):

, в этот момент вы можете вызвать.read() для него при использовании в качестве open() функции:

>>> with patch('builtins.open', mock_open(read_data='{"disabled":True}')) as filemock:
...     with open("john.json") as fh:
...         print(fh.read())
...
{"disabled":True}

Аргумент new - это объект, который заменит оригинал при исправлении.Если оставить значение по умолчанию, вместо него будет использоваться new_callable().Вы не хотите new_callable() здесь.

...