HaskellのHspecでテストケースぶん回したいの巻
Hspecって(使いづらいけど)楽しいよね!
特に使いづらいと思ったのがこれ。
Hspecで関数fの振る舞いを記述するとき、テストケースとして[(a1,b1),(a2,b2),...]っていうリストを渡して「f a1 `shouldBe` b1」みたいに書きたいとか思ってもモナドに阻まれて諦めてる
— 定食屋 おろポン (@oroponya) February 15, 2014
Hspecはなにも悪くないんだけどさ。describe
とかit
とかって、なんかIOモナドとかモナドとか、そういう「よくわからない何か」の香りがするんだよね。
だってdo
で振る舞い(it
関数)を連続して書けるんだよ?
main = hspec $ do describe "test" $ do it "will pass" $ 0 `shouldBe` 0 it "will not pass" $ 0 `shouldBe` 1 test - will pass - will not pass FAILED [1] 1) test will not pass expected: 1 but got: 0
これはもう、間違いなくモナドとかモナドとかモナドとかそういう系ですよ。
ちなみに、やりたいことはこんな感じね。やっぱり、楽したいじゃん。
main = hspec $ do describe "test" $ do map (\(input,expected)-> it "try!" input `shouldBe` expected) testCases testCases = [(0,0),(0,1)]
でも、もちろんこれはコンパイル通りません。型が全然違うからね。
これくらいはLLなら、forやらeachやら回せばパツイチですよ。 だけど、Haskellにはforもeachもないんですよ。どうすればいいんだよ。
悲嘆にくれつつ、すごいHな本を読みあさっていると、こう書いてありました。(意訳)
mapMも、forMも、あるんだよ
すごいH本と、Control.Monadは偉大でした。
Prelude Control.Monad> :t mapM_ mapM_ :: Monad m => (a -> m b) -> [a] -> m () Prelude Control.Monad> :t forM_ forM_ :: Monad m => [a] -> (a -> m b) -> m ()
[a]を受け取り、aをゴニョゴニョするとモナモナする関数を全てのaに適用し、返り値は捨てる。
まさにこれだ!!! っていうかこれ、ある意味ではfor文じゃねえの??
import Control.Monad main = forM_ [1..5] print 出力 1 2 3 4 5
import Control.Monad main = forM_ [0..3] (\i -> forM_ [0..3] (\j -> print [i,j])) 出力 [0,0] [0,1] [0,2] [0,3] [1,0] [1,1] [1,2] [1,3] [2,0] [2,1] [2,2] [2,3] [3,0] [3,1] [3,2] [3,3]
FORだった!!
そしてこれを使うと、
main = hspec $ do describe "test" $ do it "will pass" $ 0 `shouldBe` 0 it "will not pass" $ 0 `shouldBe` 1 test - will pass - will not pass FAILED [1] 1) test will not pass expected: 1 but got: 0
は、こう書けますね。
main = hspec $ do describe "test" $ do forM_ tests $ \(input,expected) -> it (show input ++ " -> " ++ show expected) $ input `shouldBe` expected tests = [(0,0),(0,1)] test - 0 -> 0 - 0 -> 1 FAILED [1] 1) test 0 -> 1 expected: 1 but got: 0
参考) http://qiita.com/kei_q/items/f4bce3f94793b99f0871
Cool!!
あとはテンプレ化してGistにおいておけば楽ちん。わふー。