Как вы издеваетесь над файловой системой в C# для модульного тестирования?


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

13 120

13 ответов:

Edit: установите пакет NuGet System.IO.Abstractions.

этот пакет не существовал, когда этот ответ был первоначально принят. Оригинальный ответ приведен для исторического контекста ниже:

вы можете сделать это, создав интерфейс:

interface IFileSystem {
    bool FileExists(string fileName);
    DateTime GetCreationDate(string fileName);
}

и создание "реальной" реализации, которая использует Системы.ИО.Файл.Существует() и т. д. Затем вы можете издеваться над этим интерфейсом, используя насмешливая структура; я рекомендую упаковка.

Edit: кто-то сделал это и любезно разместил его в интернете здесь.

я использовал этот подход, чтобы высмеять DateTime.UtcNow в IClock интерфейс (действительно очень полезно для нашего тестирования, чтобы иметь возможность контролировать течение времени!), и более традиционно, ISqlDataAccess взаимодействие.

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

можно просто принять, что некоторые методы не модульного тестирования и протестировать их в отдельных медленных интеграционных / системных тестах комплект.

Install-Package System.IO.Abstractions

этой воображаемый библиотека существует сейчас, есть пакет NuGet для System. IO. Abstractions, который абстрагирует System.IO пространство имен.

существует также набор помощников по тестированию, System.IO.Abstractions.TestingHelpers, который - на момент написания - реализован только частично, но является очень хорошей отправной точкой.

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

пример:

interface IFileWrapper { bool Exists(String filePath); }

class FileWrapper: IFileWrapper
{
    bool Exists(String filePath) { return File.Exists(filePath); }        
}

class FileWrapperStub: IFileWrapper
{
    bool Exists(String filePath) 
    { return (filePath == @"C:\myfilerocks.txt"); }
}

моя рекомендация использовать http://systemwrapper.codeplex.com/ поскольку он предоставляет обертки для наиболее часто используемых типов в системном пространстве имен

Я столкнулся со следующими решениями для этого:

  • писать интеграционные тесты, а не модульные тесты. Для этого вам нужен простой способ создания папки, в которой вы можете сбрасывать материал, не беспокоясь о вмешательстве других тестов. У меня есть простой TestFolder класс, который может создать уникальную папку для каждого метода тестирования для использования.
  • написать систему из.ИО.Файл. То есть создать IFile.cs. Я нахожу, что использование этого часто заканчивается тесты, которые просто доказывают, что вы можете писать насмешливые заявления, но использовать его, когда использование ввода-вывода мало.
  • изучите слой абстракции и извлеките файл IO из класса. Создание интерфейса для этого. Остальные используют интеграционные тесты (но это будет очень мало). Это отличается от выше в том, что вместо того, чтобы делать файл.Читаю, вы пишете намерение, говорите ioThingie.loadSettings()
  • System.IO. Abstractions. я еще не использовал это, но это тот, с которым я больше всего рад играть.

Я в конечном итоге использую все методы выше, в зависимости от того, что я пишу. Но большую часть времени я в конечном итоге думаю, что абстракция ошибочна, когда я пишу модульные тесты, которые попадают в IO.

было бы трудно издеваться над файловой системой в тесте, так как API-интерфейсы .NET на самом деле не основаны на интерфейсах или расширяемых классах, которые можно было бы издеваться.

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

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

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

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

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

быстрая заметка о том, как я определяю "правильный" модульный тест. Я считаю, что модульные тесты должны подтвердить, что вы получаете ожидаемый результат (будь то исключение, вызов метода и т. д.) При условии известных входов. Этот позволяет настроить условия модульного тестирования в виде набора входных данных и / или входных состояний. Лучший способ, который я нашел для этого,-использовать интерфейсные службы и инъекцию зависимостей, чтобы каждая ответственность, внешняя по отношению к типу, предоставлялась через интерфейс, передаваемый через конструктор или свойство.

Итак, имея это в виду, вернемся к вашему вопросу. Я издевался над вызовами файловой системы, создав IFileSystemService интерфейс вместе с FileSystemService реализация, которая является просто фасадом над методы файловой системы mscorlib. Мой код использует IFileSystemService а не типы mscorlib. Это позволяет мне подключить Мой стандарт FileSystemService когда приложение работает или издеваться над IFileSystemService в моей модульные тесты. Код приложения одинаков независимо от того, как он выполняется, но базовая инфраструктура позволяет легко протестировать этот код.

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

создание интерфейса и издевательство над ним для тестирования-это самый чистый способ пойти. Однако в качестве альтернативы yo может взглянуть на Microsoft Moles основы.

вы можете сделать это с помощью Microsoft Fakes без необходимости изменять свою кодовую базу, например, потому что она уже была заморожена.

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

using Microsoft.QualityTools.Testing.Fakes;
...
using (ShimsContext.Create())
{
     System.IO.Fakes.ShimFile.ExistsString = (p) => true;
     System.IO.Fakes.ShimFile.ReadAllTextString = (p) => "your file content";

      //Your methods to test
}

общим решением является использование некоторого абстрактного API файловой системы (например, Apache Commons VFS для Java): вся логика приложения использует API, а модульный тест способен имитировать реальную файловую систему с реализацией заглушки (эмуляция в памяти или что-то в этом роде).

для C# подобный API существует:NI.ВФС который очень похож на Apache VFS V1. Он содержит реализации по умолчанию как для локальной файловой системы, так и для файловой системы в памяти (последняя может использоваться в модульных тестах из ящика.)

Я бы пошел с ответом Джейми Ide. Не пытайтесь высмеивать вещи, которые вы не писали. Будут всевозможные зависимости, о которых вы не знали - закрытые классы, не виртуальные методы и т. д.

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

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