Разработка через тестирование: хороший / приемлемый способ тестирования операций файловой системы?

14

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

Kirbinator
источник

Ответы:

13

Как вы всегда делаете в TDD с внешними ресурсами: вы создаете один или несколько интерфейсов для операций вашей файловой системы и «макете их». Вы хотите проверить свой «генератор таблиц» и код модификации метаданных, а не сами операции файловой системы (скорее всего, вы используете готовые реализации библиотек для доступа к файловой системе).

Док Браун
источник
TDD не рекомендует издеваться над реализацией тестируемого модуля. См. (E, g) solnic.eu/2014/05/22/mocking-and-ruby.html
сору
1
@soru: Это не то, что рекомендует этот ответ. Он рекомендует сначала создавать интерфейсы, а затем издеваться над интерфейсом . Итак, вы тестируете бизнес-логику, но не интерфейс файловой системы.
слеске
5
Но, похоже, бизнес-логика определяется в терминах файлов и каталогов. Поэтому то, что вызывает API файловой системы, - это то, что требует тестирования. Тестирование любой связанной с файловой бизнес-логики не требует насмешек; просто проверь это.
сору
Право @soru, поэтому вы создаете тонкий слой вокруг файлов и папок с интерфейсом, определенным так, чтобы все специфичные для домена операции выполнялись на стороне клиента, а реализация была достаточно тривиальной, чтобы быть уверенным, что она работает без модульного тестирования (интеграционные тесты все еще будет необходимо). Подобно идее Humble Dialog в коде пользовательского интерфейса, или использования фиктивного репозитория в коде, который работает с постоянными объектами.
Жюль
2
Таким образом, мы отказываемся от тестирования фактического класса, который взаимодействует с файловой системой, базой данных и т. Д. ... вместо этого мы создаем другую реализацию с тем же интерфейсом, что и макет / заглушка, но фактический класс мы оставляем без какого-либо модульного тестирования, потому что мы считаем, что мы не можем выполнить его модульное тестирование и должны вместо этого провести интеграционное тестирование. Это верно?
Андрей Савиных
11

Что плохого в том, чтобы иметь «тестовую» файловую систему?

Создайте структуру папок / каталогов шаблонов, в которой достаточно содержимого для проверки ваших операций.

Во время настройки вашего тестового модуля скопируйте эту исходную структуру (рекомендую вам заархивировать шаблон и распаковать его в тестовую область). Запустите ваши тесты. Удалите все это во время сноса.

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

Джеймс Андерсон
источник
5
Макетирование операций с файловой системой создаст намного (!) Более быстрые выполняемые тесты, чем использование реальной файловой системы, и если это более «подвержено ошибкам», является спорным, я бы сказал, что это зависит от реализации. Тем не менее, я думаю, что ваше предложение подходит для создания автоматических интеграционных тестов (что я обычно делаю в первую очередь, когда не использую TDD). Но OP специально попросил TDD, и модульные тесты TDD должны быть быстрыми.
Док Браун
1
Я думаю, что в идеале файловые системы должны иметь целый API, написанный и поддерживаемый некоторой группой, потому что вы заново изобретаете колесо, если вы делаете что-то существенное с файловой системой.
Фрэнк Хайлеман
2
@Doc Brown - я предполагаю, что он хочет выполнять операции типа dir, delete и rename, у всех из которых есть граничные случаи, которые было бы больно высмеивать. Кроме того, на современном оборудовании разархивирование нескольких небольших файлов в каталог происходит немного медленнее, чем загрузка класса Java - в конце концов, это все ввод-вывод.
Джеймс Андерсон
Чем больше я думаю о тестовой файловой системе, тем больше она мне нравится.
Фрэнк Хайлеман,
3

Это именно то, что вам определенно необходимо для тестирования интеграции, поскольку в реальных файловых системах наблюдается довольно странное поведение (например, из-за того, что Windows не позволяет удалять файл, если какой-либо процесс, включая средство удаления, открывает его).

Таким образом, подход TDD заключается в том, чтобы сначала написать интеграционный тест (строго говоря, TDD не имеет четких понятий «модульный тест» и «интеграционный тест»; это всего лишь тесты). Вполне вероятно, что этого будет достаточно; так что работа сделана, остановись, иди домой .

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

Ни в коем случае вы бы не взяли основное ядро ​​кода, который вы пишете, и «смоделировали» его, чтобы написать тесты, которые пройдут независимо от того, является ли тестируемый модуль неправильным.

Сору
источник
Этот ответ действительно дает мне перспективу - 'unit test' and 'integration test'; they are just tests.я думаю, на самом деле , это будет лучшим решением для моего случая - мне действительно нужно протестировать библиотеки файловой системы, которые я использую для крайних случаев, и как приложение должно реагировать на те. Если я переключаюсь на другую библиотеку файловой системы, мне не нужно переписывать набор макетов / тестового кода для работы с новой библиотекой, но наличие структуры папок test и интеграционных тестов сделало бы это намного проще.
tehDorf
2

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

Чтобы сохранить «интерфейс к операциям вашей файловой системы и« макетировать их »», как подсказывает ответ @Doc Brown, как можно меньше, полезно использовать двоичные потоки Java или средство чтения текста (или их эквивалент в c # или язык программирования, который вы используете) вместо использования файлов с именами файлов непосредственно в вашем tdd-разработанный класс.

Пример:

Используя Java, я реализовал класс CsvReader

public class CsvReader {
    private Reader reader;

    public CsvReader(Reader reader) {
        this.reader = reader;
    }
}

Для тестирования я использовал в памяти такие данные

String contentOfCsv = "TestColumn1;TestColumn2\n"+
    "value1;value2\n";

CsvReader sut = new CsvReader(java.io.StringReader(contentOfCsv));

или встроить тестовые данные в ресурсы

CsvReader sut = new CsvReader(getClass().getResourceAsStream("/data.csv"));

В производстве я использую файловую систему

CsvReader sut = new CsvReader(new BufferedReader( new FileReader( "/import/Prices.csv" ) ));

Таким образом, мой CsvReader зависит не от файловой системы, а от абстракции «Reader», где есть реализация для файловой системы.

k3b
источник
2
Единственная проблема здесь в том, что OP говорил не о файловых операциях, а об операциях с файловой системой и операциях с метаданными - я думаю, он имел в виду что-то вроде перечисления всех файлов в каталоге, обновления некоторой информации EXIF ​​во всех файлах изображений и т. Д.
Док Браун
Это верно.
Кирбинатор
1
Вы можете создать IDirectoryLister, который имеет метод String [] List (String directory); затем FakeDirectoryLister может реализовать этот метод, просто возвращая новые String [] {".", "..", "foo.bat", "bar.exe"};
Андерс Линден
0

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

jhewlett
источник