是否可以测试方法是否是 运行 由任务启动时捕获的 SynchronizationContext?

Is it possible to test if a method is running with a captured SynchronizationContext when started by a Task?

我可能在某处遗漏了答案,或者是一些微不足道的东西,但我还没有找到。

这实际上是我试图在代码中完成的:

public static async Task CapturesContext()
{
    await Task.Run(() => DoWhatever());
}

public static async Task DoesNotCaptureContext()
{
    await Task.Run(() => DoWhatever()).ConfigureAwait(false);
}

public static void DoWhatever()
{
    //Any way to test here that it was run with/without a captured SynchronizationContext?
}

以上是一个过于简化的示例,但表达了我希望完成的任务。

目标是在非常大的代码库中清除 ConfigureAwait 的不当使用。

给定任何 运行 使用 Task 的方法是否可以通过 Assert 或单元测试之类的代码检查给定方法是否正在执行运行 使用捕获的 SynchronizationContext?

如果没有,是否有其他方法可以实现我的目标?

创建一个SynchronizationContext,在Post的实现中抛出异常并发送。或者,让它设置一个布尔值,指示 SendPost 是否被调用,允许您稍后检查该布尔值(如果您这样做,您可能想要 运行 提供的委托,否则你可能会陷入僵局)。

每当测试不应使用当前同步上下文的方法时,在测试开始时将该自定义同步上下文的实例设置为当前同步上下文。

The goal is to weed out improper usages of ConfigureAwait in a very large code base.

一些团队为此选择使用代码分析工具。有几个可用。我见过的最常见的方法是 require 每个 await 一个 ConfigureAwait,并明确指定 truefalse .这确保了每个 await 都经过审查并且上下文流是明确的。其他团队应用 "always use ConfigureAwait(false)" 的项目特定规则,并且仅依赖于无法遵循该规则的项目的代码审查。

您的示例代码的问题在于 不可能 DoWhatever 知道它是否被间接调用,因为 Task.Run。如果你重写这些方法,这就变得清楚了:

public static async Task CapturesContext()
{
  var task = Task.Run(() => DoWhatever());
  await task;
}

public static async Task DoesNotCaptureContext()
{
  var task = Task.Run(() => DoWhatever());
  var configuredTask = task.ConfigureAwait(false);
  await configuredTask;
}

重写方法的第一行应该清楚地表明 DoWhatever 不知道 CapturesContextDoesNotCaptureContext 是否会捕获上下文。请注意 "will"(将来时)- DoWhatever 完全有可能在甚至调用 ConfigureAwait(false) 之前运行并且 完成执行

现在,您可以从任务内部检查它是否运行在上下文现在。但在这种情况下,对于两个示例方法,DoWhatever 由于 Task.Run 将看不到上下文。因此,这并不能帮助您发现 CapturesContext 确实捕获了上下文这一事实; DoWhatever 看不到上下文,因此无法检测到它。

自定义 SynchronizationContext 是单元测试的一个很好的解决方案,但在运行时使用它会很尴尬,因为您确实有一些需要上下文的方法。出于这个原因,大多数团队选择依赖代码审查 and/or 代码分析工具。