2013-11-07 3 views
2

Тип нового для впрыска и модульного тестирования зависимости.Конструктор перегрузки для проведения модульных испытаний

почему часть:

Есть некоторые библиотеки DLL:

  • MyApp.Services.Common.dll
  • MyApp.Services.ProductA.dll -> имеет ISomeDependency и его реализации
  • MyApp .Services.ProductB.dll -> имеет IOtherDependency и его реализации
  • MyApp.Presentation.WindowsService.dll

Службы WindowsService ссылаются на Common.dll, чтобы упростить тестирование, управление версиями и развертывание.

Проблема заключается в том, что вся зависимость в ProductA и B.dll не может быть отправлена ​​из WindowsService в общую библиотеку DLL, поскольку это потребовало бы ссылки на сборку в WindowsService к ProductA и B (не хочет)

Таким образом, блок тест не может изолировать зависимости при вызове кода в Common.dll.

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

Это нормально?

Смотрите пример ниже:

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

public class ClassUnderTest 
{ 
    private ISomeDependency a; 
    private IOtherDependency b; 

    // constructor called by code 
    public ClassUnderTest() 
    { 
    this.a = new SomeDependency(); 
    this.b = new OtherDependency(); 
    } 
    public ClassUnderTest(ISomeDependency a, IOtherDependency b) 
    { 
    this.a = a; 
    this.b = b; 
    } 
} 

ответ

2

Я удивлен, что никто не упомянул, что вы можете использовать конструктор цепочку.

public class ClassUnderTest 
{ 
    readonly ISomeDependency a; 
    readonly IOtherDependency b; 

    public ClassUnderTest() : this(new SomeDependency(), new OtherDependency()) 
    { 
    } 

    public ClassUnderTest(ISomeDependency a, IOtherDependency b) 
    { 
    this.a = a; 
    this.b = b; 
    } 
} 

Хотя я принципиально согласен со всеми, кто сказал, что наилучшим подходом является контейнер МОК.

1

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

Однако, если вы не используете контейнер IOC и вместо этого выполняете «ручную инъекцию зависимостей», я не вижу ничего плохого в текущем шаблоне. Это нормально, чтобы создать «крючки» в вашем коде для более легкого тестирования.

1

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

Там есть несколько способов сделать это:

  • места коды конфигурации DI контейнера в Common.dll и тестовый проект должен ссылаться на общий для составления объектов (common.dll имеет несколько пробных подмостей конфигурационный код)
  • тестового проект имеет ссылки на ProductA.dll и ProductB.dll, и имеет конфигурацию DI контейнера для сборки объектов для тестирования, которые отделят от Common.dll

реальной проблема заключается в вашем конструкторе. Для правильной проверки необходимо, чтобы тестовый код и производственный код использовали один и тот же конструктор.Вот почему существует контейнер зависимостей Injection: для обработки сведений о том, какие объекты создаются для каждой ссылки на интерфейс.

0

но реальный код вызывает конструктор

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

Хотя я согласен с другими ответами и призываю вас идти с контейнером IoC, если вы действительно хотите сохранить рисунок, который в настоящее время есть то, по крайней мере, выставить зависимости, созданные внутри по умолчанию ctor как общественные свойства так что вы можете проверить их:

public class ClassUnderTest 
{ 
    readonly ISomeDependency a; 
    readonly IOtherDependency b; 

    public ISomeDependency A{Get{return a;}} 
    public ISomeDependency B{Get{return b;}} 

    // constructor called by code 
    public ClassUnderTest() 
    { 
    this.a = new SomeDependency(); 
    this.b = new OtherDependency(); 
    } 
} 

[TestMethod] 
public void DefaultCTOR_CreatesDependencies() 
{ 
    var sut = new ClassUnderTest(); 
    Assert.IsNotNull(sut.A,"sut didn't create SomeDependency A"); 
    Assert.IsNotNull(sut.B,"sut didn't create OtherDependency B"); 
} 

Также ссылки на ссылки.

Если вы переместили ISomeDependency и IOtherDependency в их собственной сборки (не реализаций), то вы будете иметь возможность ссылаться на эту библиотеку DLL с обеих ProductA и ProductB сборок, а также от вашего Common

  • Это обеспечивает вы со свободным соединением, потому что ваша межсетевая сборка осуществляется только по интерфейсу.
  • Вы вдруг обнаружите, что DI становится гораздо понятнее (и проще) с точки зрения того, где поставить инжектор
  • Все, что вам нужно сделать, это сделать еще один проект, который ссылается все и он может разрешить запросы на ISomeDependency в конкретные объекты, и дают эти объекты Common, ProductA и ProductBчисто по интерфейсу, и они не будут ни мудрее :)
Смежные вопросы