Модульное тестирование CompositePresentationEvent при использовании Dispatcher - PullRequest
1 голос
/ 28 января 2011

Я использую библиотеку приложений Prism / Composite и пытаюсь выполнить модульное тестирование некоторого кода, который подписывается на CompositePresentationEvent, используя EventAggregator.Код, который вызывает событие, вызывает его в другом потоке, поэтому я подписываюсь на событие, используя ThreadOption.UIThread.

Когда событие вызывает обратный вызов, оно использует диспетчер приложения, чтобы поместить его в поток пользовательского интерфейса.Это нормально во время обычного выполнения, но во время модульного тестирования диспетчер отсутствует.Код в CompositePresentationEvent выглядит следующим образом:

    private IDispatcherFacade UIDispatcher
    {
        get
        {
            if (uiDispatcher == null)
            {
                this.uiDispatcher = new DefaultDispatcher();
            }

            return uiDispatcher;
        }
    }



public class DefaultDispatcher : IDispatcherFacade
{
    /// <summary>
    /// Forwards the BeginInvoke to the current application's <see cref="Dispatcher"/>.
    /// </summary>
    /// <param name="method">Method to be invoked.</param>
    /// <param name="arg">Arguments to pass to the invoked method.</param>
    public void BeginInvoke(Delegate method, object arg)
    {
        if (Application.Current != null)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, method, arg);
        }
    }
}

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

Кто-нибудь имелУспешное юнит-тестирование в такой ситуации?Какие-нибудь советы или обходные пути, чтобы запустить диспетчера в жизнь?

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

1 Ответ

2 голосов
/ 28 января 2011

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

  1. Что кодВы тестируете подписчиков вообще
  2. То, что тестируемый код реагирует на события соответствующим образом

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

В этом тесте я просто утверждаю, что Subscribe был вызван в конструкторе, но ваш тест может быть более сложным, если вы хотите проверить классреакция на возбужденное событие.Тест показывает CompositePresentationEvent<int>.

//Arrange
Mock<MyEvent> mockEvent = new Mock<MyEvent>();
Mock<IEventAggregator> mockAggregator = new Mock<IEventAggregator>();

mockEvent.Setup
(
     evnt => evnt.Subscribe(It.IsAny<Action<int>>())
);
mockAggregator.Setup
(
    agg => agg.GetEvent<MyEvent>()
              .Returns(mockEvent.Object);
);

//Act
MyClassIWantToTest target = new MyClassIWantToTest(mockAggregator.Object);

//Assert
mockEvent.VerifyAll();

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

Редактировать: после прочтения вашего вопроса я вижу, что вы пытаетесь проверитьcallback.

В этом примере я проверяю, установлено ли для свойства "CurrentValueProperty" какое-либо значение, переданное в методе обратного вызова.Вот этот пример:

//Arrange
Mock<MyEvent> mockEvent = new Mock<MyEvent>();
Mock<IEventAggregator> mockAggregator = new Mock<IEventAggregator>();
Action<int> theEventCallback = null;

mockEvent.Setup
(
    evnt => evnt.Subscribe(It.IsAny<Action<int>>())
)
.Callback<Action<int>>
(
    cb => theEventCallback = cb
);


mockAggregator.Setup
(
    agg => agg.GetEvent<MyEvent>()
)
.Returns(mockEvent.Object);

//Act
MyClassIWantToTest target = new MyClassIWantToTest(mockAggregator.Object);

//we expect this to be populated by the callback specified in our mock setup
//that will be triggered when Subscribe is called in 
//MyClassIWantToTest's constructor
theEventCallback(27);

//Assert
Assert.AreEqual(target.CurrentValueProperty, 27);

Вот и все.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...