Я хотел бы протестировать следующий асинхронный рабочий процесс (с 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 [email protected](Unit unitVar) in ExplorationTests.fs: line 71
at [email protected](AsyncParams`1 args)
На этот раз трассировка стека показывает, где именно произошла ошибка: ExplorationTests.foo@line 71.
Есть ли способ избавиться от Async.Catch и соответствия между двумя вариантами, сохраняя при этом полезные трассировки стека? Есть ли лучший способ структурировать тесты асинхронного рабочего процесса?
Async.Catch
— единственный вариант. - person John Palmer   schedule 13.08.2013