Модификации файлов модулей

Общей задачей в программах, над которыми я работал в последнее время, является некоторая модификация текстового файла. (Привет, я нахожусь в Linux, все в файле, и я делаю крупномасштабный системный администратор.)

Но файл, который изменяет код, может отсутствовать на моем рабочем столе. И я, вероятно, не хочу изменять его, если он находится на моем рабочем столе.

Я читал об модульном тестировании в Dive Into Python, и довольно ясно, что я хочу делать, тестируя приложение, которое преобразует десятичные числа в Roman Numerals (пример в DintoP). Тестирование хорошо самодостаточно. Вам не нужно проверять правильность программы PRINTS, вам просто нужно проверить, что функции возвращают правильный вывод на данный вход.

В моем случае, однако, нам нужно проверить, что программа правильно модифицирует свою среду. Вот что я придумал:

1) Создайте "оригинальный" файл в стандартном местоположении, возможно, /tmp.

2) Запустите функцию, которая изменяет файл, передавая ему путь к файлу в /tmp.

3) Убедитесь, что файл в /tmp был правильно изменен; pass/fail unit test соответственно.

Это кажется мне клочковым. (Получает даже kludgier, если вы хотите убедиться, что резервные копии файла созданы правильно и т.д.) Кто-нибудь придумал лучший способ?

Ответ 1

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

Вместо этого просмотрите фрагменты ( "единицы" ) вашей программы. Например, у вас будет функция, определяющая, где файлы должны быть записаны? Какие входы в эту функцию? Возможно, переменная среды, возможно, некоторые значения читаются из файла конфигурации? Протестируйте эту функцию и фактически не делайте ничего, что изменяет файловую систему. Не передавайте его "реалистичные" значения, передайте ему значения, которые легко проверить. Создайте временный каталог, заполните его в тестовом методе setUp.

Затем проверьте код, который записывает файлы. Просто убедитесь, что он записывает содержимое содержимого содержимого. Даже не пишите в настоящую файловую систему! Для этого вам не нужно создавать "поддельные" файловые объекты, просто используйте удобные StringIO модули Python; они являются "реальными" реализациями "файлового" интерфейса, они просто не те, что ваша программа на самом деле собирается писать.

В конечном итоге вам придется протестировать последнюю, все-на-самом-подключенную для реального уровня функцию верхнего уровня, которая передает реальную переменную среды и реальный файл конфигурации и объединяет все вместе. Но не беспокойтесь об этом, чтобы начать. Во-первых, вы начнете собирать трюки, когда вы пишете индивидуальные тесты для небольших функций, а создание тестовых макетов, подделок и заглушек станет для вас второй натурой. Для другого: даже если вы не можете понять, как проверить этот вызов функции, у вас будет очень высокий уровень уверенности в том, что все, что он вызывает, отлично работает. Кроме того, вы заметите, что разработка, основанная на тестах, заставляет вас сделать ваши API более четкими и гибкими. Например: гораздо проще проверить что-то, что вызывает метод open() для объекта, который откуда-то был абстрактным, чем для проверки того, что вызывает os.open на строке, которую вы передаете. Метод open является гибким; его можно подделать, его можно реализовать по-разному, но строка - это строка, а os.open не дает вам никакой возможности поймать, какие методы вызывают на ней.

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

Ответ 2

У вас есть два уровня тестирования.

  • Фильтрация и изменение содержимого. Это "низкоуровневые" операции, которые не требуют физического ввода-вывода. Это тесты, принятие решений, альтернативы и т.д. "Логика" приложения.

  • Операции файловой системы. Создание, копирование, переименование, удаление, резервное копирование. Извините, но это правильные операции с файловой системой, которые - ну, требуют правильной файловой системы для тестирования.

Для такого тестирования мы часто используем объект "Mock". Вы можете создать класс "FileSystemOperations", который воплощает различные операции с файловой системой. Вы проверяете это, чтобы убедиться, что он выполняет базовое чтение, запись, копирование, переименование и т.д. В этом нет никакой реальной логики. Просто методы, которые вызывают операции файловой системы.

Затем вы можете создать MockFileSystem, которая маскирует различные операции. Вы можете использовать этот объект Mock для тестирования других классов.

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

Поместите ваш модуль MockOS в PYTHONPATH, и вы можете скрыть реальный модуль ОС.

Для производственных операций вы используете свои хорошо проверенные классы "Логика" плюс ваш класс FileSystemOperations (или настоящий модуль ОС.)

Ответ 3

Для более поздних читателей, которые просто хотят, чтобы проверить правильность написания кода на файлы, вот "fake_open", который исправляет открытый встроенный модуль для использования StringIO. fake_open возвращает файл открытых файлов, который может быть рассмотрен в unit test или doctest, без реальной файловой системы.

def fake_open(module):
    """Patch module `open` builtin so that it returns StringIOs instead of
    creating real files, which is useful for testing. Returns a dict that maps
    opened file names to StringIO objects."""
    from contextlib import closing
    from StringIO import StringIO
    streams = {}
    def fakeopen(filename,mode):
        stream = StringIO()
        stream.close = lambda: None
        streams[filename] = stream
        return closing(stream)
    module.open = fakeopen
    return streams

Ответ 4

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

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

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

Ответ 5

Я думаю, что ты на правильном пути. В зависимости от того, что вам нужно сделать chroot может помочь вам настроить среду для ваших scrpits, которая "выглядит" реальной, но не.

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

В производственном прогоне корневой путь равен /. Для тестирования вы создаете теневую среду в /tmp/test, а затем запускаете свои скрипты с корневым путем/tmp/test.

Ответ 6

Возможно, вы захотите настроить тест, чтобы он работал внутри chroot-тюрьмы, поэтому у вас есть вся среда, в которой нуждается тест, даже если пути и расположение файлов жестко закодированы в коде [не очень хорошая практика, но иногда одна получает файлы из других мест...], а затем проверяет результаты с помощью кода выхода.