我正在尝试在我的应用程序中测试一些使用Prism事件聚合器的行为。我试图进行单元测试的代码之一是订阅UI线程上的事件。深入研究事件聚集器的实现,我发现它是通过SynchronizationContext.Post实现的。
我认为这个答案可能是一个很好的解决方案,但最后我使用了一个更简单的修复方法:在单元测试开始时显式地设置同步上下文,直到您尝试读取SynchronizationContext.Current为止。
这导致了我无法完全理解的行为:
//set the sync context
var thisSyncContext = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(thisSyncContext);
thisSyncContext.Post(cb => {
var ctx = SynchronizationContext.Current; //<-- this is null
var equals = thisSyncContext.Equals(ctx); //<-- this is false
},null);
thisSyncContext.Send(cb => {
var ctx = SynchronizationContext.Current; //<-- this is not null
var equals = thisSyncContext.Equals(ctx); //<-- this is true
}, null);我知道Post是异步发生的,发送是同步发生的,当我在线程调试窗口中看到它时,它实际上会转到另一个线程ID中,就像您期望异步调用所做的那样。
我想我想要理解的是,当我告诉同步上下文执行一个函数时,无论是同步的还是异步的,我都希望能够保留这个上下文。它用于同步调用,但不用于异步调用。
为什么会显示这种行为,以及如何在单元测试中对其进行补偿?
发布于 2015-07-14 06:46:45
好的。所以我想在这篇文章的大量帮助下,我已经解决了这个问题。
如果您查看EventAggregator的来源,当您使用ThreadOption.UiThread时,您将SynchronizationContext.Current告诉Post。
在WPF应用程序中运行时,SynchronizationContext.Current是DispatcherSynchronizationContext的一个实例,它的员额的实施异步地将我们踢回原来的UI线程,正如我们所期望的那样。
在我的示例(和我的单元测试)中,我没有使用DispatcherSynchronizationContext -我使用的是普通简SynchronizationContext,它的Post的默认实现调用ThreadPool.QueueUserWorkItem。这是一种令人困惑的默认实现考虑到这些文件 --它可能真的应该是一个抽象的方法。
无论如何,这个实现产生了一个新线程,新线程获得了一个新的ExecutionContext,而该执行上下文的同步上下文默认情况下,为空。。
我想这里要注意的是,Prism并不关心同步上下文是什么类型--它只需要一个引用就可以存在当解析EventAggregator时,它第一次访问它。
因此,这里的解决方案是创建我们自己的同步上下文,它用同步行为替换预期的异步行为。
/// <summary>
/// Prism's UI thread option works by invoking Post on the current synchronization context.
/// When we do that, base.Post actually looses SynchronizationContext.Current
/// because the work has been delegated to ThreadPool.QueueUserWorkItem.
/// This implementation makes our async-intended call behave synchronously,
/// so we can preserve and verify sync contexts for callbacks during our unit tests.
/// </summary>
internal class MockSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
d(state);
}
}为了进行单元测试,我不需要事件发布的异步响应,但我确实需要验证用于UI线程的订阅是否在启动单元测试的线程上执行。
现在,当我们运行以下代码时:
//set the sync context
var thisSyncContext = new MockSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(thisSyncContext);
thisSyncContext.Post(cb => {
var ctx = SynchronizationContext.Current; //<-- this is not null
var equals = thisSyncContext.Equals(ctx); //<-- this is true
},null);
thisSyncContext.Send(cb => {
var ctx = SynchronizationContext.Current; //<-- this is not null
var equals = thisSyncContext.Equals(ctx); //<-- this is true
}, null);https://stackoverflow.com/questions/31397489
复制相似问题