Unit-testing the undefined evaluated in lazy expression in Haskell

The problem is that evaluate doesn't force your expression to NH or even WHNF1. Try x <- evaluate (take 1 $ map (+1) [undefined, 2, 3]) in GHCi - it doesn't give you any error! The only reason it does when you paste in evaluate (take 1 $ map (+1) [undefined, 2, 3]) is that GHCi also tries to print the result of what it got and, to do that, it ends up trying to evaluate the expression.

If you want to see how much of a thunk has been evaluated, you can always use :sprint in GHCi:

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

As you can see, evaluate hasn't forced the expression far enough to realize x contains an undefined. A quick fix is to evaluate the thing you are examining to normal form using 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 lets you trigger the evaluation of thunks until the argument is full evaluated. Note that it has an NFData (stands for "normal form data") constraint on it, so you may find yourself deriving Generic and NFData for your data structures.

1 Thanks for @AlexisKing for pointing out that evaluate does push its argument into WNHF, which is why head $ map (+1) [undefined, 2, 3] does trigger the error. In the case of take, it isn't enough though.