定食屋おろポン

おろしポン酢と青ネギはかけ放題です

HaskellのHspecでテストケースぶん回したいの巻

Hspecって(使いづらいけど)楽しいよね!

特に使いづらいと思ったのがこれ。

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

FORだ! - クラウザー様 - Hatena Serif

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だ! - クラウザー様 - Hatena Serif

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においておけば楽ちん。わふー。

gist9542555