Я пишу обертку для ConfigParser
в Python, чтобы обеспечить удобный интерфейс для хранения и получения настроек приложения.
Оболочка имеет два метода: read
и write
, а также набор свойств для разных параметров приложения.
Метод write
является всего лишь оболочкой для метода ConfigParser
write
с добавлением также создания файлового объекта, необходимого для ConfigParser
. Это выглядит так:
def write(self):
f = open(self.path, "w")
try:
self.config_parser.write(f)
finally:
f.close()
Я хотел бы написать unit test, который утверждает, что этот метод вызывает IOError, если файл не может быть записан, а в другом случае, когда был вызван метод записи парсера конфигурации.
Второй тест довольно прост в обращении с макетным объектом. Но вызов open
делает вещи немного сложными. В конце концов, мне нужно создать файл-объект для передачи в парсер конфигурации. Тот факт, что файл действительно будет создан при запуске этого кода, не делает его очень полезным для unit test. Есть ли какая-то стратегия для насмешливого создания файла? Может ли этот фрагмент кода каким-то образом протестировать? Или это слишком просто для тестирования?
Во-первых, вам действительно не нужно unit test open()
, так как довольно разумно предположить, что стандартная библиотека верна.
Далее вы не хотите делать манипуляции с файловой системой, чтобы получить open()
для генерации необходимой вам ошибки, потому что тогда вы не тестируете устройство, вы выполняете функциональный/интеграционный тест, включая файловую систему.
Таким образом, вы могли бы заменить open()
в глобальном пространстве имен суррогатом, который просто увеличивает значение IOError
. Хотя, вероятно, нужно убедиться, что вы вернетесь, если выполнение продолжается.
Но в конце концов, какое значение имеет тест? Там так мало в этом фрагменте кода, что ваша собственная система. Даже замена open()
действительно заканчивается тем, что является тестом, в котором говорится: "Работает ли инструкция try
и finally
в Python?"
Мое предложение? Просто добавьте инструкцию в docstring, которая записывает ваши ожидания. "Вызывает IOError, если файл не может быть записан". Затем двигайтесь дальше. Вы можете добавить unit test позже, если этот метод получит некоторую сложность (и заслуживает тестирования).
На самом деле, только open может вызывать исключение в вашем коде. Документы для write() ничего не говорят об исключениях. Возможно, только ValueError или что-то для плохого указателя файла (в результате открытого сбоя, что не может быть здесь).
Сделать IOError для открытия легко. Просто создайте файл в другом месте и откройте его для записи. Или вы можете изменить разрешения для него, чтобы у вас не было доступа.
Вы могли бы использовать здесь инструкцию with
, и она будет обрабатывать закрытие.
В python 2.5 вам нужна первая строка. В более поздних версиях вам это не нужно.
from __future__ import with_statement # python 2.5 only
def write(self):
with open(self.path, 'w') as f:
self.config_parser.write(f)
Метод записи гарантированно вызывается, если open
преуспевает и не будет вызываться, если open
вызывает IOError
. Я не знаю, зачем вам нужен тест, чтобы узнать, вызвана ли запись. В коде говорится, что это так. Не переусердствуйте.;)
Помните, что вам не нужно тестировать работу open() или ConfigParser, они не являются частью вашего кода. Вам просто нужно проверить, что вы используете их правильно. Вы можете обезопасить модуль с помощью своего собственного open(), точно так же, как и для атрибута экземпляра, и можете возвращать макет из него, который поможет вам протестировать.
Однако модульные тесты не являются моим единственным инструментом, и это одна из функций, достаточно простая для анализа и "доказывания" & dagger; что она работает.
& dagger; Менее строго, чем математики, я уверен, но достаточно хорош для меня.
IOError
еслиopen()
вызываетIOError
. И хотя я согласен с PEP257, для строк документации есть нечто большее, чем просто вводная фраза. Это определенно нормальное соглашение - помещать любую документацию, которую вы хотите, в строку документации в сочетании со вступительным заявлением. Имея это в виду, вы можете поместить его в качестве комментария, если считаете его только внутренним, или правильно поместить его в строку документации, если считаете его контрактом с пользователем метода. ( задокументированное поведение)