2010-01-12 28 views
1

Я пишу обертку для ConfigParser в Python, чтобы обеспечить удобный интерфейс для хранения и получения настроек приложения.Файл тестирования модулей пишите в Python

Обертка имеет два метода: read и write, а также набор свойств для различных параметров приложения.

write метод просто обертка для write метода ConfigParser «s с добавлением также создает объект файла, необходимую ConfigParser. Это выглядит следующим образом:

def write(self): 
    f = open(self.path, "w") 
    try: 
     self.config_parser.write(f) 
    finally: 
     f.close() 

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

Второй тест довольно прост в обращении с макетным объектом. Но вызов open делает вещи немного сложными. В конце концов, мне нужно создать файл-объект для передачи в парсер конфигурации. Тот факт, что файл действительно будет создан при запуске этого кода, не делает его очень полезным для модульного теста. Есть ли какая-то стратегия для насмешливого создания файла? Может ли этот фрагмент кода каким-то образом протестировать? Или это просто слишком просто для тестирования?

ответ

6

Во-первых, вам действительно не нужно тестировать устройство open(), так как довольно разумно предположить, что стандартная библиотека правильная.

Далее вы не хотите делать манипуляции с файловой системой, чтобы получить open(), чтобы генерировать требуемую ошибку, потому что тогда вы не тестируете устройство, вы выполняете функциональный/интеграционный тест, включая файловую систему.

Таким образом, вы могли бы заменить open() в глобальном пространстве имен суррогатом, который вызывает только IOError. Хотя, вероятно, нужно убедиться, что вы вернете вещи, если выполнение продолжается.

Но в конце концов, какое значение имеет тест? В этом фрагменте кода так мало, что это ваша собственная система. Даже замена open() действительно заканчивается тем, что является тестом, в котором говорится: «Делает ли try и finally заявление в Python?»

Мое предложение? Просто добавьте инструкцию в docstring, которая записывает ваши ожидания. «Вызывает IOError, если файл не может быть записан». Затем двигайтесь дальше. Вы можете добавить модульный тест позже, если этот метод приобретет некоторую сложность (и заслуживает тестирования).

+0

На самом деле это не должно быть в docstring, потому что он ничего не говорит о функции. * Из PEP257: «Он предписывает эффект функции или метода как команду (« Сделать это »,« Вернуть это »), а не как описание, например, не писать« Возвращает путь ... ». * В лучший случай, это должно быть «Raise IOError if ...», но это неправильно, потому что это не ваша функция, которая делает это. –

+0

Это не так. Разрешить распространение ошибки без ловли - это «ререйз». Этот метод вызывает 'IOError', если' open() 'вызывает' IOError'. И хотя я согласен с PEP257, есть больше докстерий, чем * просто * вводная фраза. Это, безусловно, нормальное соглашение о том, чтобы разместить какую-либо документацию, которую вы хотите, в docstring в сочетании с вступительным заявлением. Имея это в виду, вы можете поместить его там как комментарий, если вы считаете его только внутренним или правильно помещаете его в docstring, если считаете его контрактом с пользователем метода. (a * documented * behavior) –

+1

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

4

На самом деле, только 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. Я не знаю, зачем вам нужен тест, чтобы узнать, вызвана ли запись. В коде говорится, что это так. Не переусердствуйте. ;)

+0

1) Я не хочу вообще включать файловую систему по причинам, о которых говорил Тревис Брэдшоу в своем ответе. 2) Работает ли оператор with в Python 2.4? 3) Возможно, вы правы в том, что этот код делает так мало, поэтому в тестировании бессмысленно. –

+0

Не так уж мало. Это то, что он не делает ничего, что может потерпеть неудачу. В худшем случае он возвращает IOError по причинам, которые не беспокоят эту функцию (self.path должен знать, что она держит). ;) Вы должны вместо этого протестировать config_parser.write() отдельно и убедиться, что он работает. –

2

Помните, что вы не должны проверить, что открытые() или ConfigParser работы — они не являются частью кода — вы просто должны проверить, что вы правильно их использовать. Вы можете monkeypatch модуля с вашим собственным open(), точно так же, как и для атрибута экземпляра, и можете вернуть из него макет, который поможет вам протестировать.

Однако модульные тесты не являются моим единственным инструментом, и это одна из функций, которая достаточно проста для анализа и «доказывания» & dagger; что он работает.

& dagger; Менее строго, чем математики, я уверен, но достаточно хорош для меня.

Смежные вопросы