使用Dispatcher时单元测试CompositePresentationEvent

时间:2011-01-27 21:27:02

标签: .net unit-testing prism dispatcher

我使用Prism / Composite Application Library并尝试使用EventAggregator对一些订阅CompositePresentationEvent的代码进行单元测试。引发事件的代码是在另一个线程上引发它,所以我使用ThreadOption.UIThread订阅事件。

当事件引发回调时,它使用应用程序调度程序将其放到UI线程上。这在正常执行期间很好,但在单元测试期间没有调度程序。 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绑定,如果没有Application正在运行,则此调度程序不执行任何操作。

有没有人在这种情况下有任何成功的单元测试?将调度员带入生活的任何提示或解决方法?

我知道我可以在内部进行回调并允许我的单元测试调用此方法,但我不想更改我的代码并将此方法作为最后的手段。

1 个答案:

答案 0 :(得分:2)

您没有发布测试,所以我不清楚您要测试的是什么,但最有可能的是您尝试测试以下内容之一:

  1. 您正在测试的代码完全订阅
  2. 您正在测试的代码会对事件作出适当的反应
  3. 在任何一种情况下,您都希望模拟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();
    

    这是基础知识。这里的经验法则是,如果您的测试依赖于难以提供的系统资源,请将其与测试隔离开来。

    编辑:阅读完问题后,我发现您正在尝试测试回调。

    在此示例中,我测试“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);
    

    就是这样。