2014-06-11 22 views
4

我正在使用NUnit在Visual Studio 2013中使用Resharper的測試運行器對F#代碼進行單元測試。在項目和函數中有幾個F#記錄和區分的聯合類型現在,當我寫一個測試情況下(使用FSUnit)F#和NUnit:測試失敗時記錄類型的結構化輸出

type MyRecord = { A : int; B : int } 
let f() = { A = 4; B = 7 } 

類似如下::這些類型的情況下,例如

[<Test>] let ``test that always fails``() = f() |> should be { A = 77; B = 99 } 

我相處的線極不有用輸出消息「預期:MyNamespace.MyRecord,但找到:MyNamespace.MyRecord」。

問題當然在於NUnit顯然使用object.ToString()來生成輸出消息,而我希望它使用sprintf "%A" object來生成更有用的輸出消息,如「預期:{A = 77; B = 99}但發現:{A = 4; B = 7}「。

我知道我可能會覆蓋ToString所有記錄和識別聯合類型是這樣的:

type MyRecord = { A : int; B : int } with override this.ToString() = sprintf "%A" this 

然而,這種雜波了類型定義與代碼,僅需要用於測試目的,有點違反了唐當複製&將此代碼粘貼到所有類型定義時,請重複自己的原則。還要注意,在類型增加中添加重寫成員在F#3.1中已棄用。

老問題

所以,問題是:有沒有一種方式來獲得結構化輸出未能NUnit的測試,沒有明確覆蓋ToString? NUnit有一些插件機制嗎?我檢查過NUnit的消息來源,似乎沒有明顯的或支持的方式。該解決方案不必很快 - 畢竟,代碼只能在失敗的單元測試中執行。但是,它應該是通用的,適用於未來添加的所有F#記錄和區分的聯合類型,而不需要更改代碼。謝謝。

新問題

正如馬克·西曼在評論中指出的那樣,引文結束庫是不與非結構化輸出問題的替代方法。所以我會用Unquote取代FSUnit。但是,Unquote還存在另一個問題:它使用F#的反射庫來評估和減少歧視聯盟。顯然,反射不能處理另一個庫中定義的內部類型,導致所有的單元測試失敗並出現異常。解決方法是將所有類型公開,顯然這並不理想。

所以我的設置是:一個包含應該測試的內部類型的庫;該庫使用InternalsVisibileTo屬性使其內部對測試程序集可見。當執行測試時,Unquote使用F#的反射,反過來似乎無法正確反映內部類型。

是否有另一種已知的解決方法?

+2

我不知道您是否可以解決該特定堆棧的問題,但我想推薦[Unquote ](https://code.google.com/p/unquote)而不是FSUnit。用Unquote,你不會有這個問題。 –

+0

謝謝。雖然我確實喜歡Unquote背後的想法,但不幸的是,它並沒有解決我的問題:當我使用'=?'運算符時,我也會得到非結構化的ToString()輸出,當我使用'test <@ ... @>'深入Unquote庫內。但是,它對於簡單的測試用例工作正常。 –

+0

我不太清楚你的意思,但這是我的工作:http://blog.ploeh.dk/2014/03/21/composed-assertions-with-unquote –

回答

4

您可以覆蓋NUnit的提供的消息:

let shouldEqual (x: 'T) (y: 'T) = 
    Assert.AreEqual(x, y, sprintf "Expected: %A\nActual: %A" x y) 

,它將正常工作儘可能sprintf "%A"進去F#。

還有一個理由更喜歡這個版本should equal。這是類型安全的,並且不會導致通用值的神祕失敗測試(請參閱FsUnit `should equal` fails on `Some []`Weird None behaviour in type providers