Файловый ввод-вывод поблочного тестирования

Абстрактные классы имеют много потенциальных ловушек. Например, при переопределении метода, super(), метод не называют, если Вы явно не называете его. Это может вызвать проблемы для плохо реализованных переопределяющих классов. Кроме того, существуют потенциальные проблемы с equals() при использовании наследования.

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

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

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

60
задан Shaun Hamman 25 February 2010 в 17:18
поделиться

3 ответа

Ознакомьтесь с Учебником по TDD с использованием Rhino Mocks и SystemWrapper .

SystemWrapper является оболочкой для многих классов System.IO, включая File, FileInfo, Directory, 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());
}
35
ответ дан 24 November 2019 в 17:50
поделиться

Q1:

У вас есть три варианта.

Вариант 1: Живите с этим.

(без примера: P)

Вариант 2: Создайте небольшую абстракцию, где требуется.

Вместо выполнения файлового ввода-вывода (File.ReadAllBytes или что-то еще) в тестируемом методе вы можете изменить его так, чтобы ввод-вывод выполнялся снаружи, а вместо него передавался поток.

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:

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

11
ответ дан 24 November 2019 в 17:50
поделиться

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

1
ответ дан 24 November 2019 в 17:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: