.net 使用Dispatcher时的单元测试CompositePresentationEvent

.net 使用Dispatcher时的单元测试CompositePresentationEvent,.net,unit-testing,prism,dispatcher,.net,Unit Testing,Prism,Dispatcher,我正在使用Prism/Composite应用程序库,并尝试使用EventAggregator对订阅CompositePresentationEvent的一些代码进行单元测试。引发事件的代码正在另一个线程上引发事件,因此我使用ThreadOption.UIThread订阅事件 当事件引发回调时,它使用应用程序调度程序将其放入UI线程。这在正常执行期间是可以的,但是在单元测试期间没有调度程序。CompositePresentationEvent中的代码如下所示: private IDispa

我正在使用Prism/Composite应用程序库,并尝试使用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);
        }
    }
}
专用IDispatcherUIDispatcher
{
得到
{
如果(uiDispatcher==null)
{
this.uiDispatcher=新的DefaultDispatcher();
}
返回调度程序;
}
}
公共类DefaultDispatcher:IDispatcherFacade
{
/// 
///将BeginInvoke转发到当前应用程序的。
/// 
///要调用的方法。
///传递给被调用方法的参数。
public void BeginInvoke(委托方法,对象参数)
{
if(Application.Current!=null)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,method,arg);
}
}
}
问题在于CompositePresentationEvent与DefaultDispatcher绑定,如果没有应用程序运行,则此dispatcher不执行任何操作

有人在这种情况下成功地进行过单元测试吗?有什么建议或解决办法可以让调度员活下来吗


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

您没有发布测试,因此我不清楚您试图测试什么,但很可能您正在尝试测试以下内容之一:

  • 您正在测试的代码根本不订阅
  • 您正在测试的代码对事件做出适当的反应
  • 在这两种情况下,您都需要模拟EventAggregator。因为事件不是您想要测试的,而是利用它的代码,所以您想要提供一个假的替代方案来完成您想要它做的事情。我将尝试提供一个很好的例子。我使用Moq,但您可以选择任何您喜欢的模拟框架

    在这个测试中,我只是断言Subscribe是在构造函数中调用的,但是如果您想测试类对引发事件的反应,那么您的测试可能会更复杂。测试显示一个
    复合表示事件

    //排列
    Mock mockEvent=new Mock();
    Mock mockAggregator=new Mock();
    mockEvent.Setup
    (
    evnt=>evnt.Subscribe(It.IsAny())
    );
    mockAggregator.Setup
    (
    agg=>agg.GetEvent()
    .Returns(mockEvent.Object);
    );
    //表演
    MyClassiWantTest目标=新的MyClassiWantTest(mockAggregator.Object);
    //断言
    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);
    
    //排列
    Mock mockEvent=new Mock();
    Mock mockAggregator=new Mock();
    动作theEventCallback=null;
    mockEvent.Setup
    (
    evnt=>evnt.Subscribe(It.IsAny())
    )
    .回拨
    (
    cb=>theEventCallback=cb
    );
    mockAggregator.Setup
    (
    agg=>agg.GetEvent()
    )
    .Returns(mockEvent.Object);
    //表演
    MyClassiWantTest目标=新的MyClassiWantTest(mockAggregator.Object);
    //我们希望它由模拟设置中指定的回调填充
    //这将在调用Subscribe时触发
    //MyClassWantTest的构造函数
    第七回(27);
    //断言
    Assert.AreEqual(target.CurrentValueProperty,27);
    

    就是这样。

    如果您试图对类进行单元测试(而不是
    CompositePresentationEvent
    ),那么在测试中引入real
    CompositePresentationEvent
    实例会使它们成为非单元。也许你不应该玩CPE,而应该将你的类与之隔离,然后使用mock/stub?是的,这很公平。我想当我能够模拟这些课程时,我有点陷入了这些课程提出的问题中。我会试试安德森的建议,看看能否解决。谢谢就我努力实现的目标而言,你对第二点的看法是正确的。由于某些原因,我不能使用模拟框架,但我已经有一个模拟事件聚合器,应该能够扩展它来模拟演示事件。谢谢,我会试试的!我能够用类似的方法模拟我正在使用的演示事件——如果有模拟框架会更好though@Scott:我想知道为什么不能使用模拟框架。只是好奇。
    //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);