2009-12-23 1 views
10

Как написать единичный тест для метода, который использует инструкцию using?Как выполнить тестирование метода с помощью инструкции `using`?

Например, допустим, что у меня есть метод Foo.

public bool Foo() 
{ 
    using (IMyDisposableClass client = new MyDisposableClass()) 
    { 
     return client.SomeOtherMethod(); 
    } 
} 

Как я могу проверить что-то вроде кода выше?

Иногда я предпочитаю не использовать инструкцию using и Dispose() объект вручную. Я надеюсь, что кто-то покажет мне трюк, который я могу использовать.

+0

Что такое TDD?Единственное расширение, о котором я знаю, - это Test-Driven Development. –

+0

Это тестовое развитие. – Vadim

+0

Итак, ваш вопрос: «Как я могу тестировать разработку, как код выше?» –

ответ

15

Если вы построили IMyDisposableClass с использованием фабрики (введенной в родительский класс), а не с помощью нового ключевого слова, вы можете высмеять IMyDisposable и выполнить проверку вызова метода dispose.

+0

В качестве альтернативы вы можете использовать виртуальный заводский метод и протестировать специализацию этого объекта, который возвращает соответствующий макетируемый экземпляр. Когда вы смотрите на код, помните, что новый оператор является статической зависимостью - есть и другие веские причины, чтобы избежать его использования в нерабочих объектах. – kyoryu

+0

Может ли кто-нибудь представить пример того, что подразумевается под этим ответом. –

+0

@StephenPrice: В конструктор класса нагнетающим завода (IMyDisposableFactory MyDisposableFactory), а затем публично BOOL Foo() { с использованием (IMyDisposableClass клиент = MyDisposableFactory.CreateCl()) { возвращение client.SomeOtherMethod(); } } –

2

Методы обертки, подобные этому, не подлежат тестированию, поскольку вы не можете указать соответствующие предварительные условия или пост-условия.

Для того, чтобы метод проверяемым, вам придется пройти IMyDisposableClass экземпляр в метод или в классе хостинг Foo (и сделать сам хозяин класс реализации IDisposable), так что вы можете использовать тест двойной вместо реального чтобы проверить любые взаимодействия с ним.

-1

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

Если вы хотите протестировать метод MyDisposableClass 'dispose, чтобы проверить, не работает ли он, это должен быть отдельный модульный тест, построенный против MyDisposableClass.

Вам не нужно тестировать блок using { }, так как это часть языка. Вы либо верите, что он работает, либо не используете C#. :) Я не вижу необходимости писать модульный тест, чтобы убедиться, что вызывается Dispose().

+0

Я не хочу тестировать MyDisposableClass Я хочу издеваться над этим. Jeff Sternal и mcintyre321 bellow имеют хорошие предложения для меня попробовать. – Vadim

+0

Никто не хочет проверять использование блока {}. Использование блока {} представляет зависимость, которая не может быть введена, поскольку создание объекта с использованием блока {} должно выполняться в режиме «In-Line». И поскольку эта зависимость обычно относится к уровню персистентности или некоторому коду, который неуправляемый в какой-то момент, важно сделать это с помощью макета. Итак, вопрос в том, как вы DI a Using {} block, чтобы издеваться над зависимостью. – Suamere

16

Если у вас уже есть код и вы спрашиваете, как его протестировать, вы сначала не пишете свои тесты ... так что на самом деле не делаете TDD.

Однако у вас есть зависимость. Таким образом, подход TDD состоял бы в использовании Dependency Injection. Это можно сделать проще с помощью контейнера IoC, например Unity.

При выполнении TDD «правильно», ваши мыслительные процессы должны работать следующим образом в этом виде сценария:

  • мне нужно сделать Foo
  • Для этого я буду опираться на внешнюю зависимость, которая будет реализовать интерфейс (новый или уже существующий) из IMyDisposableClass
  • Поэтому я инжектировать IMyDisposableClass в класс, в котором Foo объявляется с помощью своего конструктора

Затем вы должны написать один (или более) тесты, которые не пройдут, и только тогда вы были бы в том месте, где вы писали тело функции Foo, и определить, нужно ли вам использовать блок using.

В действительности вы вполне можете знать, что да, вы будете использовать блок using. Но часть точки TDD заключается в том, что вам не нужно беспокоиться об этом, пока не доказали (через тесты), что вам нужно использовать объект, который этого требует.

После того, как вы определили, что вам нужно использовать using блок вы бы тогда хотите написать тест, который не - например, используя что-то вроде Rhino Mocks установить ожидание того, что Dispose будет вызван на макете объекта, который реализует IMyDisposableClass ,

Например (используя Rhino Mocks для издевки IMyDisposableClass).

[TestFixture] 
public class When_calling_Foo 
{ 
    [Test] 
    public void Should_call_Dispose() 
    { 
     IMyDisposableClass disposable = MockRepository 
             .GenerateMock<IMyDisposableClass>(); 

     Stuff stuff = new Stuff(disposable); 

     stuff.Foo(); 

     disposable.AssertWasCalled(x => x.Dispose()); 
    } 
} 

Класс, в котором существует ваша функция Foo, с IMyDisposableClass впрыскивается как зависимость:

public class Stuff 
{ 
    private readonly IMyDisposableClass _client; 

    public Stuff(IMyDisposableClass client) 
    { 
     _client = client; 
    } 

    public bool Foo() 
    { 
     using (_client) 
     { 
      return _client.SomeOtherMethod(); 
     } 
    } 
} 

И интерфейс IMyDisposableClass

public interface IMyDisposableClass : IDisposable 
{ 
    bool SomeOtherMethod(); 
} 
+0

Прошу прощения, Ричард, но ваш ответ не очень помогает. Я написал много кода DI и использовал множество различных контейнеров Inversion of Control, включая Unity. Я работаю над устаревшим кодом, который имеет только тесты интеграции. Мне нравится mcintyre321 ответ, где он предлагает использовать завод. Это очень полезно, я могу это реализовать. Если вы полностью прочитали мой вопрос, вы бы прочитали «Иногда я предпочитаю не использовать инструкцию using и удалять объект вручную». – Vadim

+0

Я изменил вопрос, чтобы уточнить, что это тестирование существующего кода, а не TDD. –

+0

TBH это почти тот же ответ, который я дал, за исключением того, что у меня был бы конструктор Stuff, например, Материал (Func myDisposableFactory) – mcintyre321

6

Ваш вопрос не имеет смысла. Если вы используете TDD, у вас уже должен быть тест на то, что вы написали. Требования, затем тесты, затем дизайн, затем развитие. Либо ваш код проходит ваши тесты, либо нет.

Теперь, если ваш вопрос заключается в том, как выполнить тестирование вышеприведенного кода, тогда это еще один вопрос, и я думаю, что другие плакаты ответили на него.

Иногда я думаю, что есть больше, чем словечки разработчиков :)

+2

Аминь. Невероятно смотреть, как люди постоянно борются с неправильным инструментом (регулярное выражение, LINQ) или неправильным подходом (DDD), потому что он «горячий», и они просто хотят быть «крутыми». Плюс один. – jason

0

Ваш вопрос не имеет смысла. Если вы делаете TDD, то метод, который вы опубликовали, уже уже прошел, в противном случае он даже не мог существовать. Итак, ваш вопрос не имеет смысла.

Если, с другой стороны, метод, который вы опубликовали, уже существует, но не полностью протестирован, то вы все равно не делаете TDD, и ваш вопрос о TDD тоже не имеет смысла.

В TDD просто невозможно для непроверенного кода. Период.

-1

Без спецификации Foo, как мы можем сказать, как ее проверить?

  1. Получить спецификации для Foo.
  2. Напишите тесты для обеспечения соответствия всех спецификаций и требований (или для разумных подмножеств некоторых функций может потребоваться практически бесконечное количество данных для тестирования).

Я считаю, что у вас есть второй, неявный вопрос, - вот как проверить правильность использования вашего MyDisposableClass. Выделяет объект, когда он освобождается, выходя из предложения use. Это отдельная тестовая проблема и не должна сочетаться с тестом Foo, поскольку спецификация Foo не должна ссылаться на конкретные данные конкретной реализации, такие как использование вашего MyDisposabeClass.

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