我使用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正在运行,则此调度程序不执行任何操作。
有没有人在这种情况下有任何成功的单元测试?将调度员带入生活的任何提示或解决方法?
我知道我可以在内部进行回调并允许我的单元测试调用此方法,但我不想更改我的代码并将此方法作为最后的手段。
答案 0 :(得分: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();
这是基础知识。这里的经验法则是,如果您的测试依赖于难以提供的系统资源,请将其与测试隔离开来。
编辑:阅读完问题后,我发现您正在尝试测试回调。
在此示例中,我测试“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);
就是这样。