Соответствующие тесты для шаблона локатора службы в C # - PullRequest
2 голосов
/ 16 февраля 2011

Я использовал этот шаблон локатора службы в своем приложении и реализовал его как синглтон:

Шаблон локатора службы

А теперь я хочу проверить это. Пока я написал тест, подтверждающий, что мой класс - Синглтон. Я также написал этот тест:

 [Test]
 [ExpectedException(typeof(ApplicationException))]
 public void GetService_Throws_Exception_When_Invalid_Key_Is_Provided()
 {
     locator.GetService<IRandomService>();
 }

Но мне не очень нравится последний тест, так как я никогда не буду использовать IRandomService. Поэтому я ищу более хороший способ проверить, что GetService<T> вызывает исключение. Также мне хотелось бы знать, есть ли какие-либо другие релевантные тесты, которые я мог бы написать для этого класса.

Я использую последнюю версию NUnit.

Приветствия

Ответы [ 3 ]

4 голосов
/ 16 февраля 2011

Некоторые вещи:

  • Тест для проверки того, является ли класс одиночным? Шаблон синглтона гарантирует, что попытка обработать синглтон как класс экземпляра даже не скомпилируется. Если вы не написали свой сервисный локатор что-то вроде следующего, это неправильно:

Шаблон Singleton в C #:

public class MySingleton()
{
    //Could also be a readonly field, or private with a GetInstance method
    public static MySingleton Instance {get; private set;}
    static MySingleton()
    {
        Instance = new MySingleton();
    }
    private MySingleton() { ... }
} 

...

//in external code
var mySingletonInstanceRef = MySingleTon.Instance; //right
var mySingletonInstanceRef = new MySingleton(); //does not compile

//EDIT: The thread-safe lazy-loaded singleton
 public class MySingleton()
{
    //static fields with initializers are initialized on first reference, so this behaves lazily
    public static readonly MySingleton instance = new MySingleton();
    //instead of the below you could make the field public, or have a GetInstance() method
    public static MySingleton Instance {get{return instance;}

    private MySingleton() { ... }
} 
  • Сервисный локатор является антишаблоном. Звучит здорово, но на самом деле это не решает проблемы, которые были созданы для решения. Главная проблема в том, что он тесно связывает вас с сервисным локатором; если локатор меняется, меняется каждый класс, который его использует. Напротив, Внедрение Зависимости может быть сделано без причудливой структуры; Вы просто убедитесь, что любой сложный, дорогой, многократно используемый объект передается объекту, который нуждается в нем, через его конструктор. Платформы DI / IoC просто упрощают этот процесс, обеспечивая предоставление всех известных зависимостей требуемого объекта, даже если объект в графе не может знать о зависимостях своих дочерних элементов.

  • Вы уже разработали класс. Дух TDD / BDD заключается в том, что вы пишете тесты, которые докажут, что код, который вы еще не написали, будет правильным. Я не говорю, что написание тестов сейчас не будет служить цели, но провальный тест требует, чтобы объект был открыт и исправлен, и если код уже интегрирован, вы можете нарушить другие функции.

  • В модульных тестах интенсивно используются конструкции, которые никогда не увидят производство. Существуют издевательства, заглушки, прокси-серверы и другие «помощники по тестированию», чтобы изолировать тестируемый объект от среды, в которую он обычно интегрирован, гарантируя, что, если входными данными являются A, B и C, тестируемый объект будет выполнять X независимо от даст ли то, к чему оно обычно подключается, A, B и C. Следовательно, не беспокойтесь о создании простых конструкций, таких как скелетный интерфейс, которые вы бы не использовали в производстве; до тех пор, пока это хорошее представление ввода, которое вы ожидаете в тестовом примере, это нормально.

2 голосов
/ 16 февраля 2011

Но мне не очень нравится последний тест, так как я никогда не буду использовать IRandomService.

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

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

Ну, я собираюсь ответить на другой вопрос здесь.

Является ли шаблон локатора службы злым?

Да, это чистое зло.

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

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

Кроме того, тестирование - это кошмар. Допустим, у вас есть

public class Foo {
    public Foo() { // }
    public string Bar() { // }
}

и вы хотите проверить Foo.Bar.

public void BarDoesSomething() {
    var foo = new Foo();
    Assert.Equal("Something", foo.Bar());
}

и вы запускаете тест, и вы получаете исключение

ServiceLocator could not resolve component Frob.

Что? О, это потому, что ваш конструктор выглядит так:

public Foo() {
    this.frob = ServiceLocator.GetService<Frob>();
}

И еще и еще.

избегать, избегать, избегать.

1 голос
/ 16 февраля 2011

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

Одна вещь, которую мы сделали при использовании контейнера внедрения зависимостей, состояла в том, чтобы разделить фазу подключения нашего приложения на модули, затем мы попытались бы определить корневой тип.в интеграционном тесте, чтобы убедиться, что приложение может быть подключено.Если проверка проводки не удалась, это был хороший признак того, что приложение не работало (хотя это также не доказывало, что приложение работало).Было бы сложнее делать такие вещи при использовании сервисного локатора.

Я также согласен на 100% с Джейсоном - поиск сервисов кажется хорошей идеей, но быстро становится неприятным.Они «тянут» (типы, использующие экземпляр локатора службы, должны быть связаны с ним), тогда как контейнеры Dependency Injection «выталкивают» (подавляющее большинство приложений не зависит от DI, поэтому код гораздо менее хрупок и более пригоден для повторного использования).

Пара других вещей:

  1. [ExpectedException] устарела.используйте Assert.Throws вместо
  2. ApplicationException также не рекомендуется.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...