Макет открытия с использованием context manager и получение аргумента open

#python #unit-testing #mocking

#python #модульное тестирование #издевательство

Вопрос:

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

У меня есть функция, которая делает это (вкратце):

 def func(path):
  with open(path) as f:
    json.load(f)
  

Я хочу провести модульное тестирование, и мне бы хотелось, чтобы при использовании open в качестве context manager, f это имя аргумента, который я передал в open. Проблема, с которой я сталкиваюсь, заключается в том, что я не могу имитировать метод enter в соответствующем модуле, он каждый раз ломается. Это то, что я делаю до сих пор:

 def setUp(self):
    # Setup the content of the config files for the tests
    json.load = MagicMock(side_effect=file_content)

    # Opening a file returns the name of the file
    builtins.open = MagicMock(side_effect=lambda x: x) #1
    builtins.open.__enter__ = MagicMock(side_effect=builtins.open.return_value) #2
  

MagicMock номер 1 делает то, что я ожидаю от него, когда я запускаю open («test»), он возвращает «test». Тем не менее, я не могу имитировать ввод, чтобы вернуть все, что я передал в open, это всегда завершается неудачей с AttributeError: __enter__ . Я пытался также делать

 builtins.open.return_value.__enter__ = MagicMock...
  

безуспешно. Кто-нибудь может подумать о том, как этого можно достичь? Я видел, как издеваться над магическими методами, и я подумал, что меня устраивает unittest mocking, но в этом случае я не могу найти подходящее решение. Спасибо!

Комментарии:

1. в этой ситуации __enter__ должен возвращаться сам, поэтому он должен возвращать builtins.open который вы определяете в строке выше. если вы хотите установить возвращаемое значение, то сделайте это для этой переменной

Ответ №1:

Я понял!

Мне пришлось определить side_effect динамически, но, похоже, работает нормально. Это то, что я сделал:

 def setUp(self):
    # Setup the content of the config files for the tests
    json.load = MagicMock(side_effect=file_content)

    # Opening a file returns the name of the file
    def get_mock_context(filename):
        mock_context = MagicMock()
        mock_context.__enter__.return_value = filename
        mock_context.__exit__.return_value = False
        return mock_context
    builtins.open = MagicMock(side_effect=get_mock_context)
  

Таким образом, возвращаемое значение является макетом, в котором методы __enter__ и __exit__ возвращают именно то имя файла, которое я передал при вызове open.