2009-08-05 2 views
2

При тестировании модулей всегда трудно понять, насколько низко вы придерживаетесь рамки.Есть некоторые вещи, которые вы просто не можете проверить?

Если у нас есть класс, который напрямую зависит от .NET Framework, то есть от класса System.IO.File, тогда мы не можем проверить его изолированно.

Конечно, мы можем обернуть его и ввести в зависимый класс, но затем мы начинаем обертывать каждый класс .NET! Что делать, если эта обертка не является прямым вызовом? Возможно, сначала он выполняет некоторую проверку/бизнес-логику, которую вы хотите протестировать?

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

Вот пример в C# просто чтобы проиллюстрировать мою точку зрения:

Этот класс тесно связан с .NET Framework .. то отлично, но теперь я не могу проверить его в изоляции, она требует файлов существовать и т.д. .

public class PathResolver 
{ 
    public string Resolve(string filename) 
    { 
     string completePath = string.Empty; 
     if(!File.Exists(filename)) 
     { 
      return Path.Combine(@"D:\MyExample", filename); 
     } 
     return completePath; 
    } 
} 

мы можем модульное тестирование это, делая что-то вроде:

public class PathResolver 
{ 
    private readonly IFileSystem _fileSystem; 

    public PathResolver(IFileSystem fileSystem) 
    { 
     _fileSystem = fileSystem; 
    } 

    public string Resolve(string filename) 
    { 
     string completePath = string.Empty; 
     if(!_fileSystem.Exists(filename)) 
     { 
      return _fileSystem.Combine(@"D:\MyExample", filename); 
     } 
     return completePath; 
    } 
} 

Но теперь мы не можем проверить класс "Filesystem"!

Что представляют собой мысли других народов?

ответ

1

Действительно, в какой-то момент вам придется реализовать интерфейсы, такие как IFileSystem, тем самым приклеивая ваше приложение к платформе .NET. Этот «клей-код-в-промежутке» не может быть проверен модулем. Это не проблема, поскольку код клея очень прост.

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

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

+0

Да, я думаю, ваше право, просто держите код клея как можно более простым, и если он выглядит немного сложным, позвольте нашим альтернативным методам тестирования поднять его. Это немного уменьшает ценность проведения модульных тестов, я думаю ... мы не будем знать, что мы что-то сломали в одном из этих классов, сохраняя при этом! –

5

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

+0

+1. Вам не нужно проверять, что уже было проверено, если вы не подозреваете ошибку в коде кода, что маловероятно. – RichardOD

+0

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

+0

Рамки представляют интересный случай, потому что они эффективно превращают определенные вещи, которые будут тестироваться в приложении с нуля в интеграционное тестирование (поскольку теперь вы используете «блок» из фреймворка вместо собственного кода). – Amber

0

Вы хотите проверить System.IO? Если нет, почему бы не использовать насмешливый/заглушающий подход, чтобы избавиться от зависимости?

+0

Да, это пример, который я показал ... и, следовательно, вопрос .. стоит ли насмехаться/завершать (т.е. обертывать) все классы .NET обеспечить, чтобы наш код взаимодействовал с ним? –

+0

Да, вообще говоря, вы хотите проверить только свой код.И более того, если вы тестируете метод класса A, который зависит от метода класса B, то вы действительно должны высмеивать класс B, поэтому вы проверяете только логику метода класса A (ну, есть особые случаи, когда вы проверяете взаимосвязь между модули - его называемые интеграционные тесты) – Ray

+0

Извините, я сказал «да», используя фальшивую фреймворк ... Я не хочу тестировать System.IO! Проблема с насмешкой заключается в том, что NMock2 поддерживает только интерфейсы, поэтому нам нужен интерфейс для File (которого у нас нет) .. поэтому нам нужно его обернуть и ввести в ... однако это означает, что мы начинаем обертывать множество классов .NET , который чувствует себя немного не так ... это мой вопрос .. мы все обертываем ?, оставьте его непроверенным ?, или есть другой подход? –

0

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

+0

Да, вот что показывает второй пример в моем вопросе ...! –

0

К сожалению, вы не можете проверить все в приложении при модульном тестировании. Вот почему при тестировании вы хотите, чтобы ваши тесты занимали 90% кода над проектом.

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

1

Это не обязательно незавершенного вашим зависимости от всей структуры - только те части, которые препятствуют вашей способности тестировать ваш код. Поэтому я думаю, что вы должны быть свободны, чтобы отключить операции System.IO.File, а также вызовы базы данных. Мне также очень полезно иметь интерфейс IClock, чтобы указывать время, а не называть DateTime.UtcNow - это позволяет нам контролировать время прохождения модульных тестов.

+0

Да, это был мой первоначальный подход, однако, кажется, все больше и больше вещей для обертывания, очередей сообщений, файловых систем, баз данных, дат, класса Random и т. Д. Im начинают чувствовать, что я обертываю множество классов! .. .hmm –

0

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

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

Хороший testdriver также очищает тестовый сценарий и оставляет систему в том же состоянии, что и перед тестом.

+0

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

+0

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

+0

«Ваш класс изолирован, когда он не использует какой-либо другой класс вашего проекта» Я не уверен, что это полное определение? - Я бы рассмотрел тест, который был удален, если он также не взаимодействует с файловой системой, базой данных, очередями сообщений и т. Д. В этом случае класс File взаимодействует с файловой системой ... не выдает мой тест? –

0

Если вы не можете протестировать класс, но вы можете издеваться над ним, используя изоляционную структуру (Rhino Mocks, Typemock, Moq), вы можете обмануть детали этого «неустойчивого» класса.

1

Это прекрасный пример разницы между тестированием модулей и интеграционным тестированием. Для модульного тестирования вашего PathResolver вам необходимо передать макет объекта, созданного вручную, или используя фреймворк (например, мой любимый Moq). Используя макет объекта, вы сможете проверить, вызван ли метод Combine.

Тест блок будет выглядеть следующим образом (с помощью Moq):

[Test] 
public void ShouldCombinePath() 
{ 
    IFileSystem fs = new Mock<IFileSystem>(); 

    PathResolver resolver = new PathResolver(fs.Object); 

    resolver.Resolve("Test.filename"); 

    fs.Verify(fs => fs.Combine()); 
} 

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

Но вы правы, вам все равно нужно проверить конкретный класс. Это то, что мы называем интеграционным тестированием. Я предлагаю вам создать отдельный проект под названием «MyProject.IntegrationTest» и протестировать SystemFileSystem напрямую, используя тестовые файлы, включенные в проект.

Тест интеграции должен выглядеть так

[Test] 
public void ShouldCombinePath() 
{ 
    DotNetFileSystem fileSystem = new DotNetFileSystem(); 

    string resultPath = fileSystem.Combine("Test.filename"); 

    Assert.That(resultPath, Text.Contains("@D:\MyExample\Test.filename")); 
} 

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

+0

Итак, вы согласны с моим текущим подходом и оставляете этот непроверенный код другим испытаниям, таким как интеграция/поисковая система? –

+0

Проверьте конкретную реализацию, когда она вам понадобится. Как я и предложил, используйте другой проект, потому что эти виды тестов потребовали времени. В моем текущем проекте мой интеграционный тест NHibernate занимает около 1 минуты. Я включил NHibernate в конце своего развития, и тесты действительно помогли найти проблему быстрее, чем если бы я запустил сайт и использовал пользовательский интерфейс для его проверки. Это стоит того. –

Смежные вопросы