Как правильно писать тесты 46 Цикл разработки 46 Структура проекта с тестами 51 Утверждения (Asserts) 52 Утверждения в форме ограничений 54 Категории 56

Вид материалаТесты

Содержание


Примеры реализации по шаблонам Мост+Фабрика
Подобный материал:
1   ...   39   40   41   42   43   44   45   46   47

Примеры реализации по шаблонам Мост+Фабрика25


Целью применения шаблона Мост является отделение замысла от реализации. Клиент использует абстрактную часть, описывающую замысел и не знает, как она реализована. Реализация находится в компонентах, которые предназначены для решения общей задачи, но ничего не знают друг о друге. Общая часть у компонентов – их одинаковые интерфейсы.

Замысел обычно строится так, чтобы его можно было использовать повторно.

Пример 1


Рассмотрим код, реализующий эту идею:

public interface Intention

{

void Echo(string message);

}


internal class Implementation : Intention

{

public void Echo(string message)

{

Console.WriteLine("From the console " + message);

}

}

Клиент ничего не должен знать о реализации, но пока что ему необходимо знать название реализации и следовательно иметь к ней доступ:

Intention obj = new Implementation();

obj.Echo( "hello anybody there");

Пока что это не лучший вариант.

Класс реализации был введен с областью видимости internal, это значит, что компонент, как ему и полагается, будет находиться в отдельной сборке и доступ к нему будут иметь только классы этой сборки. Следовательно, чтобы клиент мог получить экземпляр класса реализации, понадобится класс-помощник. Этим классом будет фабрика.

public class Factory

{

public static Intention Instantiate()

{

return new Implementation();

}

}

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

Создадим для нашего кода тест.

[TestMethod]

public void SimpleBridge()

{

Intention obj = Factory.Instantiate();

obj.Echo("hello anybody there");

}


Этот тест будет без проблем выполняться, но он пока что бесполезен, потому что отсутствует способ проверки правильности выводимых данных через метод Echo. В данном случае понадобится mock-объект, т.е. объект, ведущий себя как объект, которому он подражает, но не обладающий его функциональностью. В этом случае нужно сделать mock-объект для класса Console. Обычно mock-объекты создаются для низкоуровневых классов, от которых зависит тестируемый код, чтобы изолированно провести тестирование.

Наш «поддельный» класс Console придется поместить в отдельное пространство имен:

namespace MockObjects

{

public class Console

{

public Console() { }

public static void WriteLine(string message)

{

System.Console.WriteLine("-" + message + "-");

}

}

}

Чтобы его использовать вместе с классом Implementation, нужно указать псевдоним для пространства имен Console:

#define TEST_BUILD


using System;


#if TEST_BUILD

using Console = MockObjects.Console;

#endif

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

В нашем примере для выполнения теста не нужна вся функциональность класса Console. Нам нужно дополнить mock-объект необходимой функциональностью для создания обратной связи, благодаря которой, мы сможем проверить, правильно ли происходит вывод на консоль или нет. Для этого введем в класс Console callback-метод:

using System;


namespace MockObjects

{

public delegate void FeedbackString(string message);


class NoCallbackDefinedException : Exception

{

public NoCallbackDefinedException() : base("No callback is defined") { }

}


public class Callback

{

private static FeedbackString _feedback;

public static FeedbackString CBFeedbackString

{

get

{

if (_feedback == null)

{

throw new NoCallbackDefinedException();

}

return _feedback;

}

set

{

_feedback = value;

}

}

}


public class Console

{

public Console() { }

public static void WriteLine(string message)

{

Callback.CBFeedbackString(message);

}

}

}

Основная идея заключается в том, что метод WriteLine перенаправляет вызов в метод делегата, который обязательно должен предоставить клиент mock-объекта. Делегат должен, например, содержать проверку правильности вывода строки на консоль. При этом вывода на консоль не происходит, потому что вместо System.Console используется mock-объект Console.

private string _strHelloAnybodyThere = "hello anybody there";


[TestMethod]

public void SimpleBridge()

{

Intention obj = Factory.Instantiate();

MockObjects.Callback.CBFeedbackString =

new MockObjects.FeedbackString(this.CallbackSimpleBridge);

obj.Echo(_strHelloAnybodyThere);

}

void CallbackSimpleBridge(string message)

{

string test = "From the console " + _strHelloAnybodyThere;

if (message != test)

{

throw new Exception();

}

}

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