2016-12-14 34 views
1

在遇到undefined時,在Haskell中編寫一個表達式失敗的單元測試有點棘手。我試了下HSpec:單元測試在Haskell中延遲表達式中評估的undefined

module Main where 

import Test.Hspec 
import Control.Exception (evaluate) 

main :: IO() 
main = hspec $ do 
    describe "Test" $ do 
    it "test case" $ do 
     evaluate (take 1 $ map (+1) [undefined, 2, 3]) `shouldThrow` anyException 

無濟於事。它報告我did not get expected exception: SomeException

如果我評價REPL同樣的表情,我會得到:

[*** Exception: Prelude.undefined 
CallStack (from HasCallStack): 
    error, called at libraries\base\GHC\Err.hs:79:14 in base:GHC.Err 
    undefined, called at <interactive>:2:20 in interactive:Ghci1 

回答

4

的問題是,evaluate不會強迫你的表達NH 甚至WHNF 。在GHCi中嘗試x <- evaluate (take 1 $ map (+1) [undefined, 2, 3]) - 它不會給你任何錯誤!當你粘貼evaluate (take 1 $ map (+1) [undefined, 2, 3])時,它唯一的原因是GHCi也會嘗試打印它所得到的結果,爲此,它最終試圖評估表達式。

如果你想看到怎樣一個thunk的多少進行了評估,你可以隨時在GHCI使用:sprint

ghci> x <- evaluate (take 1 $ map (+1) [undefined, 2, 3]) 
ghci> :sprint x 
x = [_] 

正如你所看到的,evaluate並不強迫表達遠遠不夠實現x包含undefined。快速解決方法是使用force評估您正在檢查的事物。

import Test.Hspec 
import Control.Exception (evaluate) 
import Control.DeepSeq (force) 

main :: IO() 
main = hspec $ do 
    describe "Test" $ do 
    it "test case" $ do 
     evaluate (force (take 1 $ map (+1) [undefined, 2, 3] :: [Int])) 
     `shouldThrow` anyException 

force讓你觸發thunk的評估,直到參數完整評估。請注意,它有一個NFData(代表「標準格式數據」)約束,因此您可能會發現自己爲您的數據結構導出了GenericNFData


感謝@AlexisKing用於指出evaluate確實推動其參數爲WNHF,這就是爲什麼head $ map (+1) [undefined, 2, 3]不會引發錯誤。在take的情況下,這是不夠的。

+0

這個答案很好,但是當你說[評估]沒有評估爲WHNF時[我的文檔明確聲明它評估它的參數爲WHNF],我很困惑(https://hackage.haskell.org /package/base-4.9.0.0/docs/Control-Exception.html#v:evaluate)。 –

+0

@AlexisKing我的立場正確!無論如何,WNHF還不夠,所以你需要'NFData'。 – Alec

+0

所以,實際上,測試通過,因爲它產生一個單一的未定義元素的列表?爲什麼不應用(+1)函數? –