Как выполнить модульное тестирование метода с помощью оператора using? - PullRequest
12 голосов
/ 23 декабря 2009

Как мне написать модульный тест для метода с оператором using?

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

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

Как мне проверить что-то похожее на код выше?

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

Ответы [ 7 ]

17 голосов
/ 23 декабря 2009

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

public bool Foo()
{
    using (IMyDisposableClass client = _myDisposableClassFactory.Create())
    {
        return client.SomeOtherMethod();
    }
}
16 голосов
/ 23 декабря 2009

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

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

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

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

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

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

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

Например (использование насмешек Rhino для насмешки 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();
}
6 голосов
/ 23 декабря 2009

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

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

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

2 голосов
/ 23 декабря 2009

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

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

0 голосов
/ 24 декабря 2009

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

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

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

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

0 голосов
/ 23 декабря 2009

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

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

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

0 голосов
/ 23 декабря 2009

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

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

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

...