从单元测试调用调度程序时,传播在延续任务中抛出的异常

时间:2014-01-21 11:54:04

标签: c# wpf unit-testing task-parallel-library

我在单元测试中执行以下代码:

        var continueScheduler = new CurrentThreadScheduler();
        var task = Task.Factory
            .StartNew(() => { })
            .ContinueWith(obj => { throw new Exception("Fail"); }, continueScheduler);

        while (!task.IsCompleted)
        {
            DoEvents();
            Thread.Sleep(10);
        }

StartNew()启动一个新线程,执行空操作。 CurrentThreadScheduler然后确保在主线程上执行ContinueWith()操作:

    public class CurrentThreadScheduler : TaskScheduler
    {
        private readonly Dispatcher _dispatcher;

        public CurrentThreadScheduler()
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
        }

        protected override void QueueTask(Task task)
        {
            _dispatcher.BeginInvoke(new Func<bool>(() => TryExecuteTask(task)));
        }

        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            return true;
        }

        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return Enumerable.Empty<Task>();
        }
    }

while循环等待任务(包括ContinueWith())完成。这是DoEvents()代码:

    private static void DoEvents()
    {
        var frame = new DispatcherFrame();
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
        Dispatcher.PushFrame(frame);
    }

    private static object ExitFrame(object frame)
    {
        ((DispatcherFrame)frame).Continue = false;
        return null;
    }

问题:

我希望在ContinueWith()操作中抛出异常以使测试失败。问题是BeginInvoke()中的CurrentThreadScheduler.QueueTask()吞下了异常,我无法找到检测它的方法。

我曾尝试订阅Dispatcher.CurrentDispatcher.UnhandledException,但事件处理程序永远不会被调用。我试图使用Invoke()代替BeginInvoke(),希望异常会传播,但没有成功。

毋庸置疑,此问题中的代码已经过简化,以证明我的问题。

1 个答案:

答案 0 :(得分:1)

OP在这里。

我的调试技巧需要改进(事情之中)。问题不是Dispatcher.BeginInvoke(),而是我对结果的处理方式。

QueueTask的这种实现解决了这个问题:

protected override void QueueTask(Task task)
{
    var method = new Func<bool>(delegate
        {
            bool success = TryExecuteTask(task);
            if (task.IsFaulted)
            {
                throw new Exception("Invoke", task.Exception);
            }
            return success;
        });
    _dispatcher.BeginInvoke(method);
}

现在,听Dispatcher.CurrentDispatcher.UnhandledException正常工作:

Dispatcher.CurrentDispatcher.UnhandledException += OnDispatcherUnhandledException;

private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    Assert.Fail("Exception: {0}", e.Exception);
}

那就是说,我同意@Noseratio的评论,即更好的解决方案是不在单元测试代码中使用WPF Dispatcher。