Модульное Тестирование Файла Ввода / Вывода
читая существующие потоки, связанные с модульным тестированием здесь, в Stack Overflow, я не мог найти ни одного с четким ответом о том, как выполнять операции ввода-вывода файлов модульного тестирования. Я только недавно начал изучать модульное тестирование, ранее зная о преимуществах, но с трудом привыкаю к написанию тестов в первую очередь. Я настроил свой проект на использование NUnit и Rhino Mocks, и хотя я понимаю концепцию, стоящую за ними, у меня возникли небольшие проблемы с пониманием того, как использовать фиктивный объект.
в частности, у меня есть два вопроса, на которые я хотел бы ответить. Во-первых, как правильно выполнять операции ввода-вывода файлов модульного теста? Во-вторых, в моих попытках узнать о модульном тестировании я столкнулся с инъекцией зависимостей. После настройки и работы Ninject мне было интересно, следует ли использовать DI в своих модульных тестах или просто создавать экземпляры объектов напрямую.
5 ответов:
проверить учебник для TDD используя Носорог Издевается и SystemWrapper.
SystemWrapper обертывает многие из System.IO классы, включая файл, FileInfo, каталог, DirectoryInfo,... . Вы можете видеть полный список.
в этом уроке я показываю, как сделать тестирование с MbUnit, но это точно то же самое для NUnit.
ваш тест будет выглядеть примерно так:
[Test] public void When_try_to_create_directory_that_already_exists_return_false() { var directoryInfoStub = MockRepository.GenerateStub<IDirectoryInfoWrap>(); directoryInfoStub.Stub(x => x.Exists).Return(true); Assert.AreEqual(false, new DirectoryInfoSample().TryToCreateDirectory(directoryInfoStub)); directoryInfoStub.AssertWasNotCalled(x => x.Create()); }
не обязательно один что нужно делать при тестировании файловой системы. На самом деле, есть несколько вещей, которые вы могли бы сделать, в зависимости от обстоятельств.
вопрос, который вам нужно задать:что я тестирую?
что файловая система работает? вам, вероятно, не нужно тестировать что Если вы не используете операционную систему, с которой вы крайне незнакомы. Так что если вы просто например, давать команду для сохранения файлов-это пустая трата времени на написание теста, чтобы убедиться, что они действительно сохраняются.
что файлы сохраняются в нужном месте? Ну, как вы знаете, что такое правильное место? Предположительно у вас есть код, который сочетает в себе путь с именем файла. Это код, который вы можете легко проверить: ваш вход-две строки, а ваш выход должен быть строкой, которая является допустимым местоположением файла, построенным с использованием этих двух веревка.
что вы получаете правильный набор файлов из каталога? вам, вероятно, придется написать тест для вашего класса file-getter, который действительно проверяет файловую систему. Но вы должны использовать тестовый каталог с файлами в нем, которые не будут меняться. Вы также должны поместить этот тест в проект интеграционного теста, потому что это не настоящий модульный тест, потому что он зависит от файловой системы.
но, мне нужно что-то делать с файлами, которые я получаю. на что тест, вы должны использовать подделка для вашего класса file-getter. Ваша подделка должна вернуть жестко закодированный список файлов. Если вы используете real file-getter и A real file-processor, вы не будете знать, какой из них вызывает сбой теста. Таким образом, ваш класс файлового процессора при тестировании должен использовать поддельный класс File-getter. Ваш класс файлового процессора должен принимать файл-геттер интерфейс. В реале код, который вы передадите в настоящий файл-добытчик. В тестовом коде вы передадите поддельный файл-геттер, который возвращает известный статический список.
основные принципы:
- используйте поддельную файловую систему, скрытую за интерфейсом, когда вы не тестируете саму файловую систему.
- Если вам нужно проверить реальные операции с файлами, то
- пометить тест как интеграционный тест, а не тест.
- есть места испытания каталог, набор файлов и т. д. это всегда будет там в неизменном состоянии, поэтому ваши файл-ориентированные интеграционные тесты могут проходить последовательно.
Вопрос 1:
у вас есть три варианта.
Вариант 1: Жить с этим.
(нет примера: P)
Вариант 2: при необходимости создайте небольшую абстракцию.
вместо того, чтобы делать файл ввода / вывода (файл.ReadAllBytes или что-то еще) в тестируемом методе вы можете изменить его так, чтобы IO выполнялся снаружи, а вместо этого передавался поток.
public class MyClassThatOpensFiles { public bool IsDataValid(string filename) { var filebytes = File.ReadAllBytes(filename); DoSomethingWithFile(fileBytes); } }будет стань
// File IO is done outside prior to this call, so in the level // above the caller would open a file and pass in the stream public class MyClassThatNoLongerOpensFiles { public bool IsDataValid(Stream stream) // or byte[] { DoSomethingWithStreamInstead(stream); // can be a memorystream in tests } }этот подход является компромиссом. Во-первых, да, это более проверяемо. Однако он торгует тестируемостью для небольшого добавления к сложности. Это может ударить по ремонтопригодности и количеству кода, который вы должны написать, плюс вы можете просто переместить свою проблему тестирования на один уровень.
Вариант 3: Оберните всю файловую систему
делая еще один шаг, издевательство над файловой системой может быть правильным подходом; это зависит от того, насколько вы готовы жить.
Я прошел этот маршрут раньше; у меня была реализация обернутой файловой системы, но в конце концов я просто удалил ее. Существуют тонкие различия в API, я должен был ввести его везде и в конечном счете это была дополнительная боль для небольшой выгоды, так как многие из классов, использующих его, не были очень важны для меня. Если бы я использовал контейнер IoC или писал что-то критическое, и тесты должны были быть быстрыми, я бы, возможно, застрял с ним. Как и во всех этих вариантах, ваш пробег может отличаться.
Что касается вашего вопроса контейнера МОК:
впрысните ваши двойники теста вручную. Если вам нужно сделать много монотонной работы, просто использовать методы установки / фабрики в ваших тестах. Использование контейнера IoC для тестирования было бы чрезмерным в крайнем случае! Может быть, я не понимаю ваш второй вопрос, хотя.
В настоящее время я потребляю объект IFileSystem через инъекцию зависимостей. Для производственного кода класс-оболочка реализует интерфейс, обертывая определенные функции ввода-вывода, которые мне нужны. При тестировании я могу создать реализацию null или stub и предоставить ее тестируемому классу. Тестируемый класс ничего не знает.
С 2012 года, вы можете сделать это с помощью 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 }
Comments