Як ви знущаєтесь з файлової системи в C # для тестування одиниць?


149

Чи є бібліотеки або методи, щоб знущатися з файлової системи в C # для написання одиничних тестів? У моєму теперішньому випадку у мене є методи, які перевіряють, чи існує певний файл, і читають дату створення. Мені може знадобитися більше, ніж це в майбутньому.


1
Це виглядає як дублікат кількох інших, в тому числі: stackoverflow.com/questions/664277 / ... .
Джон Сондерс

Можливо, спробуйте вивчити pex ( research.microsoft.com/en-us/projects/pex/filesystem.pdf )
Tinus

2
@Mitch: Більшу частину часу достатньо розмістити дані у файловій системі та дозволити тестуванню одиниць виконувати свій курс. Однак я стикався з методами, які виконують багато операцій вводу-виводу, і налаштування тестового середовища для таких методів значно спрощується за допомогою використання макетної файлової системи.
Стів Гуїді

Я писав github.com/guillaume86/VirtualPath для цієї мети (і багато іншого), це все ще WIP і API, безумовно, зміниться, але він вже працює, і деякі тести включені.
Guillaume86

Відповіді:


154

Редагувати: Встановіть пакет NuGet System.IO.Abstractions.

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

Ви можете зробити це, створивши інтерфейс:

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

і створення «реальної» реалізації, яка використовує System.IO.File.Exists () тощо. Потім ви можете знущатися над цим інтерфейсом, використовуючи глузуючий фреймворк; Я рекомендую Moq .

Редагувати: хтось це зробив і люб’язно опублікував це тут .

Я використовував такий підхід для знущання над DateTime.UtcNow в інтерфейсі IClock (дійсно дуже корисно для нашого тестування, щоб мати можливість контролювати витрату часу!), А традиційно - інтерфейс ISqlDataAccess.

Іншим підходом може бути використання TypeMock , це дозволяє перехоплювати дзвінки до класів та заглушувати їх. Це, однак, коштує грошей, і для його запуску потрібно встановити його на ПК всієї вашої команди та сервер збирання, але, мабуть, це не буде працювати для System.IO.File, оскільки він не може заглушити mscorlib .

Ви також можете просто прийняти, що певні методи не перевіряються в одиницях, і перевірити їх в окремому наборі повільних інтеграційних / системних тестів.


1
На мою думку, створення інтерфейсу, як описав Метт, це шлях. Я навіть написав інструмент, який генерує такі інтерфейси для вас, який корисний при спробі знущатися над статичними та / або герметичними класами, або недетермінованими методами (наприклад, годинниками та генераторами випадкових чисел). Див. Jolt.codeplex.com для отримання додаткової інформації.
Стів Гуїді

Схоже, репо з цієї статті було видалено / переміщено без попереднього повідомлення. Однак тут, мабуть, існує цілий пакет його зусиль: nuget.org/packages/mscorlib-mock
Майк-Е

Typemock має обмеження щодо того, які типи є фальшивими, але (принаймні, у поточній версії станом на жовтень 2017 року) ви, безумовно, можете підробити статичний клас File. Я просто це перевірив сам.
Райан Родемойер

Чи можете ви підсумувати деякі тестові набори інтеграції?
Озкан

83

Install-Package System.IO.Abstractions

Ця уявна бібліотека існує зараз, існує пакет NuGet для System.IO.Abstractions , який абстрагує простір імен System.IO.

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


3
Я думаю, що стандартизація навколо цієї вже побудованої абстракції - найкраща ставка. Ніколи не чув про цю бібліотеку, тому велике спасибі за голову вгору.
julealgon

Прем'єр - це менеджер пакунків .. відкрити ... Інструменти> NuGet Package Manager> Console Package Manager
thedanotto

11

Ймовірно, вам доведеться скласти контракт, щоб визначити, що вам потрібно з файлової системи, а потім написати обгортку навколо цих функцій. У цей момент ви зможете знущатись або стримувати виконання.

Приклад:

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"); }
}

5

Моя рекомендація - використовувати http://systemwrapper.codeplex.com/, оскільки вона надає обгортки для використовуваних в основному типів у просторі імен системи


Зараз я використовую цю бібліотеку, і тепер, коли я виявив, що її абстракції для таких речей, як FileStream, не включають IDisposable, я шукаю заміну. Якщо бібліотека не дозволяє мені належним чином розпоряджатися потоками, я не можу рекомендувати (або використовувати) її для обробки таких операцій.
James Nail

1
IFileStreamWrap SystemWrapper зараз реалізує ідентифікатор.
tster

systemwrapper - це лише .net Framework, це спричинить дивні проблеми при використанні з .netcore
Аділ Х. Раза

3

Я натрапив на таке рішення цього питання:

  • Пишіть інтеграційні тести, а не одиничні тести. Для цього вам потрібен простий спосіб створення папки, в яку ви можете скидати матеріали, не турбуючись про те, що інші тести заважають. У мене простий клас TestFolder, який може створити унікальну папку методів тестування, яку слід використовувати.
  • Напишіть макетну систему System.IO.File. Тобто створити IFile.cs . Я вважаю, що використання цього часто закінчується тестами, які просто доводять, що ви можете писати глузуючі заяви, але використовуйте його, коли використання IO невелике.
  • Вивчіть шар абстракції та витягніть файл ІО з класу. Створіть для цього інтерфейс. Решта використовують тести інтеграції (але це буде дуже мало). Це відрізняється від вище, тим, що замість того, щоб робити файл.
  • System.IO.Abstractions . Я ще цього не використовував, але саме з цим я найбільше хвилююся.

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


4
Посилання на IFile.cs порушено.
Майк-Е

3

Використовуючи System.IO.Abstractions та System.IO.Abstractions.TestingHelpers так:

public class ManageFile {
   private readonly IFileSystem _fileSystem;
   public ManageFile(IFileSystem fileSystem){

      _fileSystem = fileSystem;
   }

   public bool FileExists(string filePath){}
       if(_fileSystem.File.Exists(filePath){
          return true;
       }
       return false;
   }
}

У своєму тестовому класі ви використовуєте MockFileSystem () для знущання над файлом і інстанціюєте ManageFile, як:

var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);

2

Це можна зробити за допомогою Microsoft Fakes без необхідності змінювати базу коду, наприклад, оскільки вона вже була заморожена.

Спершу генеруйте підроблену збірку для System.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
}

1

Було б важко знущатися над тестовою файловою системою, оскільки API файлів .NET насправді не засновані на інтерфейсах або розширюваних класах, з яких можна знущатися.

Однак якщо у вас є власний функціональний рівень для доступу до файлової системи, ви можете знущатися над цим у тесті на одиницю.

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


1

Я не впевнений, як би ви знущалися над файловою системою. Що ви можете зробити, це написати налаштування тестового кріплення, яке створює папку тощо з необхідною структурою для тестів. Метод схованки очистить його після запуску тестів.

Відредаговано, щоб додати: Роздумуючи над цим трохи більше, я не думаю, що ви хочете знущатися над файловою системою для тестування цього типу методів. Якщо ви знущаєтесь над тим, щоб файлова система повертала справжнє значення, якщо певний файл існує, і використовуєте його у вашому тесті методу, який перевіряє, чи існує цей файл, ви не тестуєте багато нічого. Якщо глузування з файлової системи було б корисним, якщо ви хочете протестувати метод, який залежав від файлової системи, але активність файлової системи не була невід'ємною частиною методу, який перевіряється.


1

Щоб відповісти на ваше конкретне запитання: Ні, немає жодної бібліотеки, яка дозволить вам знущатися над файловими дзвінками вводу / виводу (про які я знаю). Це означає, що для правильного тестування блоків, які перевіряють ваші типи, потрібно буде врахувати це обмеження під час визначення типів.

Швидка примітка про те, як я визначаю "правильний" тест на одиницю. Я вважаю, що одиничні тести повинні підтвердити отримання очікуваного результату (будь то виняток, використання методу тощо) за умови відомих даних. Це дозволяє встановити умови тестування пристрою як набір входів та / або станів введення. Найкращий спосіб, який я знайшов для цього, - це використання сервісів на основі інтерфейсу та введення залежностей, щоб кожна відповідальність, яка є зовнішньою для типу, забезпечувалася через інтерфейс, переданий через конструктор або властивість.

Отже, маючи це на увазі, поверніться до вашого питання. Я знущався над дзвінками файлової системи, створюючи IFileSystemServiceінтерфейс разом із FileSystemServiceреалізацією, яка є просто фасадом над методами файлової системи mscorlib. Мій код тоді використовує, IFileSystemServiceа не типи mscorlib. Це дозволяє мені підключити мій стандарт, FileSystemServiceколи програма працює або знущається над IFileSystemServiceтестами мого модуля. Код програми однаковий незалежно від способу його запуску, однак базова інфраструктура дозволяє легко перевірити цей код.

Я визнаю, що використовувати оболонку навколо об'єктів файлової системи mscorlib боляче, але, у цих конкретних сценаріях, варто додатково працювати, оскільки тестування стає набагато простішим та надійнішим.


1

Створення інтерфейсу та глузування з нього для тестування - це найчистіший шлях. Однак як альтернативу можна поглянути на рамки Microsoft Moles .


0

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

Для C # існує аналогічний API: NI.Vfs, який дуже схожий на Apache VFS V1. Він містить реалізацію за замовчуванням як для локальної файлової системи, так і для файлової системи в пам'яті (останню можна використовувати в одиничних тестах з поля).


-1

В даний час ми використовуємо фірмовий двигун даних, і його API не піддається впливу інтерфейсів, тому ми навряд чи зможемо перевірити наш код доступу до даних. Тоді я пішов і з підходом Метта та Джозефа.


-2

Я б пішов з відповіддю Джеймі Ідея. Не намагайтеся висміювати речі, про які ви не писали. Будуть усілякі залежності, про які ви не знали - герметичні класи, не віртуальні методи тощо.

Іншим підходом було б обернути прихильні методи чимось, що є глузливим. наприклад, створити клас під назвою FileWrapper, який дозволяє отримати доступ до методів File, але це те, з чого можна знущатися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.