Sai*_*ino 3 .net c# unit-testing synchronizationcontext
我正在尝试对使用 Prism 事件聚合器的应用程序中的某些行为进行单元测试。我尝试进行单元测试的代码所做的一件事是订阅 UI 线程上的事件。深入研究EventAggregator 的实现,我发现它是通过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);
Run Code Online (Sandbox Code Playgroud)
我知道 Post 是异步发生的,Send 是同步发生的,当我在线程调试窗口中观察它时,它实际上会切换到不同的线程 ID,正如您期望的异步调用那样。
我想我想理解的是,当我告诉同步上下文执行一个函数时,无论是同步还是异步,我都希望保留该上下文。它保留用于同步调用,但不适用于异步调用。
为什么会出现这种行为,我该如何在单元测试中补偿它?
好的。所以我想我想通了这一点,从这篇文章中得到了很多帮助。
如果你在寻找源EventAggregator当你Publish使用ThreadOption.UiThread,你告诉SynchronizationContext.Current来Post。
当在 WPF 应用程序中运行时,SynchronizationContext.Current是 a 的一个实例DispatcherSynchronizationContext,它的 Post 实现异步地将我们踢回到原始 UI 线程,正如我们所期望的那样..
在我的示例(以及我的单元测试)中,我没有使用 a DispatcherSynchronizationContext- 我使用的是普通的 jane SynchronizationContext,其Post的默认实现调用ThreadPool.QueueUserWorkItem. 鉴于文档,这是一种令人困惑的默认实现- 它真的很可能应该是一个抽象方法。
无论如何,这个实现会产生一个新线程,新线程获得一个新的 ExecutionContext,并且该执行上下文的同步上下文默认为 null。
我想这里要注意的一点是,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);
}
}
Run Code Online (Sandbox Code Playgroud)
出于我的单元测试的目的,我不需要事件发布的异步响应,但我确实需要验证用于 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);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
461 次 |
| 最近记录: |