Модификации файла модульного тестирования - PullRequest
22 голосов
/ 20 сентября 2008

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

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

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

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

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

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

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

Мне это кажется глупым. (Становится еще круче, если вы хотите убедиться, что резервные копии файла созданы правильно и т. Д.) Кто-нибудь придумал лучший способ?

Ответы [ 6 ]

14 голосов
/ 21 сентября 2008

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

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

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

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

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

7 голосов
/ 20 сентября 2008

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

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

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

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

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

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

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

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

3 голосов
/ 08 декабря 2009

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

def fake_open(module):
    """Patch module's `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
2 голосов
/ 20 сентября 2008

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

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

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

1 голос
/ 20 сентября 2008

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

1 голос
/ 20 сентября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...