【问题标题】:Unit Testing CompositePresentationEvent when using the Dispatcher使用 Dispatcher 时对 CompositePresentationEvent 进行单元测试
【发布时间】:2011-01-27 21:27:02
【问题描述】:

我正在使用 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);
        }
    }
}

问题在于 CompositePresentationEvent 与 DefaultDispatcher 相关联,如果没有应用程序运行,此调度程序将不执行任何操作。

有没有人在这种情况下进行过成功的单元测试?有什么技巧或解决方法可以让调度员活跃起来吗?

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

【问题讨论】:

  • 如果您尝试对您的课程进行单元测试(不是CompositePresentationEvent),那么在您的测试中引入真正的CompositePresentationEvent 实例会使它们成为非单元。也许你应该将你的类与它隔离,然后使用模拟/存根而不是玩 CPE?
  • 是的,公平点。我想当我应该能够模拟它们时,我对这些课程提出的问题感到有些困惑。我会尝试安德森的建议,看看它是否能解决它。谢谢!

标签: .net unit-testing prism dispatcher


【解决方案1】:

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

  1. 您正在测试的代码完全订阅了
  2. 您正在测试的代码对事件做出适当的反应

在任何一种情况下,您都需要模拟 EventAggregator。因为事件不是您想要测试的,而是使用它的代码,所以您想要提供一个假的替代品来做您想要它做的事情。我会尝试提供一个很好的例子。我使用 Moq,但你可以选择任何你喜欢的模拟框架。

在这个测试中,我只是断言在构造函数中调用了订阅,但是如果你想测试类对引发事件的反应,你的测试可能会更复杂。测试显示CompositePresentationEvent&lt;int&gt;

//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);

就是这样。

【讨论】:

  • 就我要实现的目标而言,您的第二个数字是正确的。由于某些原因,我不能使用模拟框架,但我已经有一个模拟事件聚合器,并且应该能够扩展它来模拟演示事件。谢谢,我试试看!
  • 我能够使用类似的方法来模拟我正在使用的 Presentation 事件 - 不过如果使用模拟框架会更好
  • @Scott:我很想知道为什么你不能使用模拟框架。只是好奇。
猜你喜欢
  • 2010-11-09
  • 2019-05-01
  • 2017-03-03
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
  • 2020-03-07
  • 2017-11-22
相关资源
最近更新 更多