我想测试以下异步工作流程(使用NUnit + FsUnit):
let foo = async {
failwith "oops"
return 42
}
我为此编写了以下测试:
let [<Test>] TestFoo () =
foo
|> Async.RunSynchronously
|> should equal 42
由于foo引发,因此在单元测试运行器中得到以下堆栈跟踪:
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
不幸的是,stacktrace并没有告诉我引发异常的位置。它在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 [email protected](Unit unitVar) in ExplorationTests.fs: line 71
at [email protected](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
测试看起来好多了,重新抛出的代码没有发生错误(和以前一样),并且当出现问题时,我在测试运行程序中获得了有用的堆栈跟踪信息。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句