Модульное Тестирование Файла Ввода / Вывода



читая существующие потоки, связанные с модульным тестированием здесь, в Stack Overflow, я не мог найти ни одного с четким ответом о том, как выполнять операции ввода-вывода файлов модульного тестирования. Я только недавно начал изучать модульное тестирование, ранее зная о преимуществах, но с трудом привыкаю к написанию тестов в первую очередь. Я настроил свой проект на использование NUnit и Rhino Mocks, и хотя я понимаю концепцию, стоящую за ними, у меня возникли небольшие проблемы с пониманием того, как использовать фиктивный объект.



в частности, у меня есть два вопроса, на которые я хотел бы ответить. Во-первых, как правильно выполнять операции ввода-вывода файлов модульного теста? Во-вторых, в моих попытках узнать о модульном тестировании я столкнулся с инъекцией зависимостей. После настройки и работы Ninject мне было интересно, следует ли использовать DI в своих модульных тестах или просто создавать экземпляры объектов напрямую.

695   5  

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

    Ничего не найдено.