Как мне смоделировать open, используемый в операторе with (используя макет фреймворка в Python)?

StackOverflow https://stackoverflow.com/questions/1289894

  •  18-09-2019
  •  | 
  •  

Вопрос

Как мне протестировать следующий код с помощью mocks (используя mocks, декоратор исправлений и sentinels, предоставленные Макет фреймворка Майкла Фурда):

def testme(filepath):
    with open(filepath, 'r') as f:
        return f.read()
Это было полезно?

Решение

Способ сделать это изменился в макете 0.7.0, который, наконец, поддерживает издевательство над методами протокола Python (магическими методами), особенно с использованием MagicMock:

http://www.voidspace.org.uk/python/mock/magicmock.html

Пример макета, открытого как менеджер контекста (со страницы примеров в документации макета):

>>> open_name = '%s.open' % __name__
>>> with patch(open_name, create=True) as mock_open:
...     mock_open.return_value = MagicMock(spec=file)
...
...     with open('/some/path', 'w') as f:
...         f.write('something')
...
<mock.Mock object at 0x...>
>>> file_handle = mock_open.return_value.__enter__.return_value
>>> file_handle.write.assert_called_with('something')

Другие советы

В этих ответах много шума;почти все правильные, но устаревшие и неаккуратные. mock_open часть mock framework и очень прост в использовании. patch используется как контекст, возвращает объект, использованный для замены исправленного:вы можете использовать его, чтобы упростить тест.

Питон 3.x

Использовать builtins вместо __builtin__.

from unittest.mock import patch, mock_open
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

Питон 2.7

mock не является частью unittest и тебе следует пропатчить __builtin__

from mock import patch, mock_open
with patch("__builtin__.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

Чехол-декоратор

Если бы вы использовали patch в качестве декоратора, используя mock_open()результат как new patchАргумент может быть немного странным.

В этом случае лучше использовать new_callable patchаргумент и помните, что все дополнительные аргументы, которые patch не использует, будет передано new_callable функционировать, как описано в patch документация.

patch() принимает произвольные аргументы ключевого слова.Они будут переданы в Mock (или new_callable) при создании.

Например, декорированная версия для Питон 3.x является:

@patch("builtins.open", new_callable=mock_open, read_data="data")
def test_patch(mock_file):
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

Помните, что в этом случае patch добавит фиктивный объект в качестве аргумента вашей тестовой функции.

С последними версиями mock вы можете использовать действительно полезные макет_открытия помощник:

mock_open(mock=Нет, read_data=Нет)

Вспомогательная функция для создания макет, чтобы заменить использование открытия.Он работает на открытой называется, прямо или используется в качестве контекстного менеджера.

Фиктивный аргумент - это фиктивный объект для настройки.Если нет (значение по умолчанию ), то для вас будет создан MagicMock с API , ограниченным методами или атрибутами, доступными в стандартных дескрипторах файлов.

read_data - это строка, возвращаемая методом чтения дескриптора файла .По умолчанию это пустая строка.

>>> from mock import mock_open, patch
>>> m = mock_open()
>>> with patch('{}.open'.format(__name__), m, create=True):
...    with open('foo', 'w') as h:
...        h.write('some stuff')

>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

Использовать макет_открыть для простого файла read() (исходный фрагмент макета Mock_open уже дано на этой странице ориентирован больше на запись):

my_text = "some text to return when read() is called on the file object"
mocked_open_function = mock.mock_open(read_data=my_text)

with mock.patch("__builtin__.open", mocked_open_function):
    with open("any_string") as f:
        print f.read()

Обратите внимание, что согласно документации дляock_open, это специально для read(), поэтому не будет работать с распространенными шаблонами, такими как for line in f, например.

Использует Python 2.6.6/mock 1.0.1.

Возможно, я немного опоздал на игру, но мне это помогло при звонке. open в другом модуле без необходимости создания нового файла.

test.py

import unittest
from mock import Mock, patch, mock_open
from MyObj import MyObj

class TestObj(unittest.TestCase):
    open_ = mock_open()
    with patch.object(__builtin__, "open", open_):
        ref = MyObj()
        ref.save("myfile.txt")
    assert open_.call_args_list == [call("myfile.txt", "wb")]

МойObj.py

class MyObj(object):
    def save(self, filename):
        with open(filename, "wb") as f:
            f.write("sample text")

Путем исправления open функционировать внутри __builtin__ модуль к моему mock_open(), я могу имитировать запись в файл, не создавая его.

Примечание:Если вы используете модуль, использующий cython, или ваша программа каким-либо образом зависит от cython, вам необходимо импортировать Цитона __builtin__ модуль включая import __builtin__ в верхней части вашего файла.Вы не сможете издеваться над универсальным __builtin__ если вы используете Cython.

Верхний ответ полезен, но я немного расширил его.

Если вы хотите установить значение вашего файлового объекта (теперь f в as f) на основе аргументов, переданных open() вот один из способов сделать это:

def save_arg_return_data(*args, **kwargs):
    mm = MagicMock(spec=file)
    mm.__enter__.return_value = do_something_with_data(*args, **kwargs)
    return mm
m = MagicMock()
m.side_effect = save_arg_return_array_of_data

# if your open() call is in the file mymodule.animals 
# use mymodule.animals as name_of_called_file
open_name = '%s.open' % name_of_called_file

with patch(open_name, m, create=True):
    #do testing here

По сути, open() вернет объект и with позвоню __enter__() на этом объекте.

Чтобы правильно издеваться, мы должны издеваться open() чтобы вернуть фиктивный объект.Этот макетный объект должен затем издеваться над __enter__() позвони туда(MagicMock сделает это за нас), чтобы вернуть желаемый объект фиктивных данных/файла (следовательно, mm.__enter__.return_value).Выполнение этого с помощью двух макетов, как описано выше, позволяет нам захватывать аргументы, передаваемые в open() и передать их нашему do_something_with_data метод.

Я передал весь макет файла в виде строки open() и мой do_something_with_data выглядело так:

def do_something_with_data(*args, **kwargs):
    return args[0].split("\n")

Это преобразует строку в список, и вы можете делать следующее, как с обычным файлом:

for line in file:
    #do action
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top