2013-08-12 51 views
13

Tôi muốn thử nghiệm các công việc sau đây async (với NUnit + FsUnit):Làm thế nào để có được một stacktrace hữu ích khi thử nghiệm F # async dòng làm việc

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

tôi đã viết các bài kiểm tra sau cho nó:

let [<Test>] TestFoo() = 
    foo 
    |> Async.RunSynchronously 
    |> should equal 42 

Kể từ foo ném tôi nhận được stacktrace sau trong runner đơn vị kiểm tra:

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 

Thật không may là stacktrace nai không nói cho tôi biết nơi ngoại lệ được nêu ra. Nó dừng tại RunSynchronously.

Một nơi nào đó tôi nghe nói rằng Async.Catch kỳ diệu khôi phục stacktrace, vì vậy tôi điều chỉnh thử nghiệm của tôi:

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)) 

Bây giờ đây là xấu xí nhưng ít nhất nó tạo ra một stacktrace hữu ích:

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) 

này thời gian stacktrace cho thấy chính xác nơi lỗi xảy ra: [email protected] 71

Có cách nào để loại bỏ Async.Catch và kết hợp giữa hai lựa chọn trong khi vẫn nhận được stacktraces hữu ích? Có cách nào tốt hơn để cấu trúc các kiểm tra quy trình làm việc không đồng bộ không?

+1

Tôi gặp sự cố tương tự và Không đồng bộ.Catch là giải pháp duy nhất tôi có thể tìm thấy –

+0

Tôi đã gửi email cho Don Syme, người đã đề xuất nó là một hạn chế .NET cơ bản và rằng 'Async.Catch' là tùy chọn duy nhất. –

+0

@ JohnPalmer nghe có vẻ như một câu trả lời cho tôi – mydogisbox

Trả lời

5

Kể từ Async.Catch và rethrowing ngoại trừ dường như là cách duy nhất để có được một stacktrace hữu ích tôi đã đưa ra như sau:

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" 

Note "ExceptionDispatchInfo.Capture (ex) .Throw () ". Đó là về cách tốt nhất người ta có thể rethrow một ngoại lệ mà không làm hỏng stacktrace của nó (nhược điểm: chỉ có sẵn từ. NET 4.5).

Bây giờ tôi có thể viết lại các thử nghiệm "TestFooWithBetterStacktrace" như thế:

let [<Test>] TestFooWithBetterStacktrace() = 
    foo 
    |> Async.Catch 
    |> Async.RunSynchronously 
    |> Async.Rethrow 
    |> should equal 42 

Xét nghiệm này trông đẹp hơn nhiều, mã rethrowing không hút (càng nhiều càng tốt trước đó) và tôi nhận được stacktraces hữu ích trong các thử nghiệm Á hậu khi có sự cố.

+0

+1 Điều này có vẻ như một cách giải quyết tốt đẹp. – Daniel

3

Trích dẫn từ một số email mà tôi gửi Don Syme một khi trở lại:

Kinh nghiệm debug sẽ được cải thiện nếu bạn cố gắng thiết lập "Catch Đầu tiên Exceptions Chance" trong Debug -> Exceptions -> CLR Exceptions . Bật "Chỉ cần mã của tôi" cũng có thể giúp bạn.

Đúng vậy. Với async {...}, các tính toán không bị ngăn xếp, do đó, ngoại lệ cần phải được sửa lại ở một số nơi để đưa chúng trở lại chủ đề phải .

Sử dụng hợp lý Async.Catch hoặc xử lý ngoại lệ khác cũng có thể trợ giúp.

+0

hi john, quote # 1 cho phép vi phạm các ngoại lệ cơ hội đầu tiên không thực sự giúp ích nhiều vì tôi hầu như không bao giờ sử dụng trình gỡ rối. tôi cần stacktraces hữu ích trong đầu ra của Á hậu thử nghiệm đơn vị. # 2 điều đó có nghĩa là tôi đã làm nó ngay trong thử nghiệm thứ 2 của tôi (TestFooWithBetterStacktrace)? – stmax

+0

@stmax - Tôi nghĩ thử nghiệm thứ 2 của bạn là tốt. –