Подтвердить что ты не робот

В Castle Windsor 3 переопределить существующую регистрацию компонента в unit test

Я пытаюсь использовать Castle Windsor в своих автоматизированных тестах так:

В каждом тесте:

  • Функция Setup() создает контейнер Windsor, регистрируя стандартные реализации каждого компонента
  • Функция Test получает доступ к компонентам с помощью метода IWindsorContainer.Resolve<T> и проверяет их поведение
  • Функция TearDown() предоставляет контейнер Windsor (и любые созданные компоненты)

Например, у меня может быть 15 тестов, которые обращаются к компонентам, которые косвенно приводят к созданию компонента IMediaPlayerProxyFactory. Функция SetUp регистрирует достаточно хорошую реализацию IMediaPlayerProxyFactory, поэтому у меня нет бремени обслуживания регистрации в каждом из 15 тестов.

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

this.WindsorContainer.Register(
                                 Component.For<IMediaPlayerProxyFactory>()
                                          .Instance(mockMediaPlayerProxyFactory)
                              );

Но Windsor выбрасывает Castle.MicroKernel.ComponentRegistrationException, с сообщением "Уже есть компонент с этим именем".

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


Согласно документации , Castle Windsor 3 позволяет переопределять регистрацию, но я могу найти только один пример:
Container.Register(
    Classes.FromThisAssembly()
        .BasedOn<IEmptyService>()
        .WithService.Base()
        .ConfigureFor<EmptyServiceA>(c => c.IsDefault()));

ConfigureFor - метод класса BasedOnDescriptor. В моем случае я не использую FromDescriptor или BasedOnDescriptor.

4b9b3361

Ответ 1

Для создания переопределяющего экземпляра вам необходимо сделать две вещи:

  • Назначьте ему уникальное имя
  • Вызвать метод IsDefault

Итак, чтобы привести пример в действие:

this.WindsorContainer.Register(
                             Component.For<IMediaPlayerProxyFactory>()
                                      .Instance(mockMediaPlayerProxyFactory)
                                      .IsDefault()
                                      .Named("OverridingFactory")
                          );

Поскольку я планирую использовать этот переопределяющий patten во многих тестах, я создал свой собственный метод расширения:

public static class TestWindsorExtensions
{
    public static ComponentRegistration<T> OverridesExistingRegistration<T>(this ComponentRegistration<T> componentRegistration) where T : class
    {
        return componentRegistration
                            .Named(Guid.NewGuid().ToString())
                            .IsDefault();
    }
}

Теперь можно упростить пример:

this.WindsorContainer.Register(
                             Component.For<IMediaPlayerProxyFactory>()
                                      .Instance(mockMediaPlayerProxyFactory)
                                      .OverridesExistingRegistration()
                          );


Позднее Редактировать

В версии 3.1 представлен метод IsFallback. Если я зарегистрирую все мои исходные компоненты с помощью IsFallback, то любые новые регистрации автоматически переопределяют эти начальные регистрации. Я бы пошел по этому пути, если бы функциональность была доступна в то время.

https://github.com/castleproject/Windsor/blob/master/docs/whats-new-3.1.md#fallback-components

Ответ 2

Не используйте контейнер повторно для тестирования. Вместо этого установите null в TearDown() и повторно инициализируйте его для каждого фактического теста.