gpt4 book ai didi

unit-testing - 测试 F# 异步工作流时如何获得有用的堆栈跟踪

转载 作者:行者123 更新时间:2023-12-03 23:53:27 25 4
gpt4 key购买 nike

我想测试以下异步工作流程(使用 NUnit+FsUnit):

let foo = async {
failwith "oops"
return 42
}

我为它编写了以下测试:
let [<Test>] TestFoo () =
foo
|> Async.RunSynchronously
|> should equal 42

由于 foo throws 我在单元测试运行程序中得到以下堆栈跟踪:
System.Exception : oops
at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously(CancellationToken token, FSharpAsync`1 computation, FSharpOption`1 timeout)
at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously(FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
at ExplorationTests.TestFoo() in ExplorationTests.fs: line 76

不幸的是,堆栈跟踪并没有告诉我在哪里引发了异常。它在 RunSynchronously 处停止。

我在某处听说 Async.Catch 神奇地恢复了堆栈跟踪,所以我调整了我的测试:
let [<Test>] TestFooWithBetterStacktrace () =
foo
|> Async.Catch
|> Async.RunSynchronously
|> fun x -> match x with
| Choice1Of2 x -> x |> should equal 42
| Choice2Of2 ex -> raise (new System.Exception(null, ex))

现在这很丑陋,但至少它产生了有用的堆栈跟踪:
System.Exception : Exception of type 'System.Exception' was thrown.
----> System.Exception : oops
at Microsoft.FSharp.Core.Operators.Raise(Exception exn)
at ExplorationTests.TestFooWithBetterStacktrace() in ExplorationTests.fs: line 86
--Exception
at Microsoft.FSharp.Core.Operators.FailWith(String message)
at ExplorationTests.foo@71.Invoke(Unit unitVar) in ExplorationTests.fs: line 71
at Microsoft.FSharp.Control.AsyncBuilderImpl.callA@769.Invoke(AsyncParams`1 args)

这次堆栈跟踪显示了错误发生的确切位置:ExplorationTests.foo@line 71

有没有办法摆脱 Async.Catch 和两个选择之间的匹配,同时仍然获得有用的堆栈跟踪?有没有更好的方法来构建异步工作流测试?

最佳答案

由于 Async.Catch 并重新抛出异常似乎是获得有用堆栈跟踪的唯一方法,因此我想出了以下内容:

type Async with
static member Rethrow x =
match x with
| Choice1Of2 x -> x
| Choice2Of2 ex -> ExceptionDispatchInfo.Capture(ex).Throw()
failwith "nothing to return, but will never get here"

注意“ExceptionDispatchInfo.Capture(ex).Throw()”。这是在不破坏其堆栈跟踪的情况下重新抛出异常的最佳方式(缺点:仅自 .NET 4.5 起可用)。

现在我可以像这样重写测试“TestFooWithBetterStacktrace”:
let [<Test>] TestFooWithBetterStacktrace () =
foo
|> Async.Catch
|> Async.RunSynchronously
|> Async.Rethrow
|> should equal 42

测试看起来好多了,重新抛出的代码并不糟糕(和以前一样),并且当出现问题时,我会在测试运行器中获得有用的堆栈跟踪。

关于unit-testing - 测试 F# 异步工作流时如何获得有用的堆栈跟踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18192328/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com