Ошибка атрибута: __exit__ когда я пытаюсь макетировать встроенные функции

#python #unit-testing #mocking #mox

#питон #модульное тестирование #насмешливый #mox #python #издевательство

Вопрос:

В настоящее время я пытаюсь протестировать встроенный метод open() в Python для теста. Тем не менее, я всегда в конечном итоге получаю сбой и это результирующее сообщение:

    File "/opt/home/venv/lib/python2.7/site-packages/nose-1.3.0-py2.7.egg/nose/result.py", line 187, in _exc_info_to_string
return _TextTestResult._exc_info_to_string(self, err, test)
 File "/opt/python-2.7.3/lib/python2.7/unittest/result.py", line 164, in _exc_info_to_string
msgLines = traceback.format_exception(exctype, value, tb)
 File "/opt/python-2.7.3/lib/python2.7/traceback.py", line 141, in format_exception
list = list   format_tb(tb, limit)
 File "/opt/python-2.7.3/lib/python2.7/traceback.py", line 76, in format_tb
return format_list(extract_tb(tb, limit))
  File "/opt/python-2.7.3/lib/python2.7/traceback.py", line 101, in extract_tb
line = linecache.getline(filename, lineno, f.f_globals)
  File "/opt/home/venv/lib/python2.7/linecache.py", line 14, in getline
lines = getlines(filename, module_globals)
 File "/opt/home/venv/lib/python2.7/linecache.py", line 40, in getlines
return updatecache(filename, module_globals)
 File "/opt/home/venv/lib/python2.7/linecache.py", line 127, in updatecache
with open(fullname, 'rU') as fp:
AttributeError: __exit__
  

Вот мой тестовый код:

 m = mox.Mox()
m.StubOutWithMock(__builtin__, 'open')
mock_file = m.CreateMock(__builtin__.file)

open(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(mock_file)
mock_file.write(mox.IgnoreArg()).MultipleTimes()
mock_file.close()

write_file_method()
  

Ответ №1:

__exit__ это метод, который вызывается при попытке закрыть файл. Ваш макет файла не обрабатывает mock_file.close() , просто open() . Вам также нужно будет смоделировать close метод.


Редактировать:

Во-вторых, почему вы хотите издеваться open ? AFAIK, вы не должны использовать этот метод. Тестируемый метод должен принимать открытый поток (вместо имени файла, например). В производственном коде клиенты отвечают за открытие файла (например, pickle.dump ). В ваших тестах вы передаете StringIO или макет объекта, который поддерживает запись.


Редактировать 2: я бы разделил ваш метод на две части и протестировал каждый бит отдельно.

  • создание файла: убедитесь, что перед вызовом этого метода файл не существует, и он существует после этого. Можно утверждать, что такой однострочный метод не стоит тестировать.
  • запись в файл: см. Выше. Создайте StringIO и запишите в него, чтобы ваши тесты могли затем проверить правильность написанного.

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

1. Итак, я добавляю строку с надписью m.StubOutWithMock ( встроенный , ‘закрыть’)? Я пробовал это, но получаю ту же ошибку, если только я не поместил ее в неправильное место (сразу после m.StubOut ‘open’)

2. Метод, который я хочу протестировать, создает файл и записывает в него, поэтому я не передаю файловый объект или что-то еще. Я не уверен, как еще это можно проверить.

3. Я не думаю, что это open / close интерфейс, по крайней мере пока. Похоже, проблема в том, что он не издевается над интерфейсом context Manager, т.е. __enter__ и __exit__ . К вашему сведению, __exit__ не вызывается при закрытии файла, все наоборот. __exit__ вызывается при выходе из диспетчера контекста ( with блока), и он, в свою очередь, вызывает close . Также похоже, что фактическая ошибка в тестовом коде проглатывается nose , потому что nose пытается открыть файл для записи ошибки с помощью синтаксиса диспетчера контекста, но исправленный open не поддерживает его.

4. Чтобы вывести __enter__ и __exit__ , это self.__mox.StubOutWithMock(__builtin__, '__enter__') и тому подобное?

5. Это методы для объекта, возвращаемого из open , а не вещи из __builtin__ напрямую. Честно говоря, я думаю, вам следует попытаться найти другой подход. Таким образом вы нарушите ведение журнала тестирования. Может быть, попробуйте написать оболочку или что-то, что исправляет локальные / глобальные значения вокруг вашей конкретной целевой функции, чтобы вы не влияли на другие библиотеки, полагающиеся на open за пределами тестируемого кода.