我想测试以下异步工作流程(使用 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 和两个选择之间的匹配,同时仍然获得有用的堆栈跟踪?有没有更好的方法来构建异步工作流测试?