ãã®è¨äºã®ææ°çã¯ãgithubã§ç®¡çããã¦ãã¾ãã
ããã¯Haskell Advent Calendar 2012ã®5æ¥ç®ã®è¨äºã§ãã
Haskellã§ä½æããããã±ã¼ã¸ã«å¯¾ãã¦ãåä½ãã¹ããæ¸ãããã®ææ°æ å ±ããå±ããã¾ãã
è¦ç´
è¦ç¹ã¯4ã¤ã§ãã
- å©ç¨è ã«è¦ãããæ¯ãèãã¯ãdoctest ã§æ¸ã
- å©ç¨è ã«è¦ããããªãæ¯ãèãã¯ãhspec ã§æ¸ã
- ãã¹ããèªååãããã¬ã¼ã ã¯ã¼ã¯ã¨ãã¦ã¯ Cabal ã使ã
- doctest ã§ã hspec ã§ããç´ç²ãªã³ã¼ãã«å¯¾ãã¦ã¯ãã§ããã ã QuickCheck ãªã©ã®æ§è³ªãã¹ããæ¸ã
ãã®è¨äºã§ä¸çªä¼ãããã®ã¯ã3) ã§ããä¾é¡ã¨ãã¦ã¯ãBase64 ã¨ãã符å·åãåãä¸ãã¾ããBase64 ã¯ç¥ã£ã¦ããã¨ä»®å®ãã¦è©±ãé²ãã¾ãã®ã§ãç¥ããªã人ã¯ããããã Wikipedia の Base64 の説明ã§ãèªãã§ä¸ããã
ãã®è¨äºã§å©ç¨ããã³ã¼ãã®å ¨ä½ã¯ãunit-test-exampleã¨ããããã±ã¼ã¸åã§githubã«ç½®ãã¦ããã¾ãã以ä¸ã®ä¾ã¯ããã®ããã±ã¼ã¸ã®ããããã£ã¬ã¯ããªã«ããã¨æ³å®ãã¦ãã¾ãã
doctest
å©ç¨è ã«è¦ãããæ¯ãèãã¯ãdoctest ã§æ¸ãã¾ããããããã°ãããã¥ã¢ã«ã«ããªãã¾ããããã¹ãã«ã使ãã¾ã(æ¬å½ã¯è¨è¨ã«ãå©ç¨ãã¦æ¬²ããã®ã§ãããä»åã¯è¸ã¿è¾¼ã¿ã¾ãã)ã
Haskell ã®ããã¥ã¡ã³ããã¼ã«ã¯ Haddock ã§ããã³ã¡ã³ãã«ããã¼ã¯ã¢ããã使ã£ã¦èª¬æãæ¸ãã¾ãã">>>" ã¨ãããã¼ã¯ã¢ããããå©ç¨ä¾ã®ããã«ç¨æããã¦ãã¾ãã
Codec.Base64 ã®ä¾ãè¦ã¦ã¿ã¾ãããã
-- | -- Base64 encoding. -- -- >>> encode "foo bar" -- "Zm9vIGJhcg==" encode :: String -> String encode = ... -- | -- Base64 decoding. -- -- >>> decode "Zm9vIGJhcg==" -- "foo bar" decode :: String -> String decode = ...
">>>" ã®æ¬¡ã®è¡ã«ã¯ãçµæãæ¸ãã¾ãã">>>" 㯠GHCi ã®ããã³ããã ã¨æã£ã¦æ§ãã¾ããããã§ã«å®è£ ãããå ´åã¯ãGHCi ã§å¯¾è©±çã«åãããä¾ãã³ãã¼ï¼ãã¼ã¹ããã¦ããã³ãããå¤æ´ããã ãã§ãã
GHCiãªã®ã§ãlet ã¨ãã使ãã¾ãããã®ä¾ã¯ç´ç²ã§ããããã¡ãã IO ã§ãOKã§ããGHCi ã§ã§ãããã¨ã¯ããã¹ã¦ã§ãã¾ãããã¹ãã¯åãªãæååæ¯è¼ãªã®ã§ãä¾å¤ãæ¸ãã¾ãã詳ãã㯠doctest ã®ããã¥ã¢ã«ãèªãã§ä¸ããã
以ä¸ã®ã³ãã³ãã§ã"dist" ã®ä¸ã« HTML ã®ããã¥ã¢ã«ãä½æã§ãã¾ãã
% cabal haddock --hyperlink-source
ããã¥ã¢ã«ã¯ãããªæãã«ãªãã¾ãã
doctest ã§ãã¹ããå®è¡ãã¦ã¿ã¾ãããã
% doctest Codec/Base64.hs Examples: 2 Tried: 2 Errors: 0 Failures: 0
doctest ãã¤ã³ã¹ãã¼ã«ãã¦ããªã人ã¯ã以ä¸ã®ã³ãã³ããå®è¡ãã¦ã¿ã¦ä¸ããã
% cabal install --enable-test --only-dependencies
ããããã°ãç¾å¨ã®ããã±ã¼ã¸ãã¤ã³ã¹ãã¼ã«ãããã¨ãªããå¿ è¦ãªã©ã¤ãã©ãª(ã³ãã³ã)ãã¤ã³ã¹ãã¼ã«ã§ãã¾ãã
hspec
å©ç¨è ã«è¦ãã¦ããããããªãæ¯ãèãã«é¢ãã¦ã¯ãhspec ã§è¨è¿°ãã¾ããã¢ã¸ã¥ã¼ã«ãã¡ã¤ã«1ã¤ã«å¯¾ãã1ã¤ã®ãã¹ããã¡ã¤ã«ãæ¸ãã®ãåºæ¬ã§ãããã¨ãã°ãBase64.hs ã«å¯¾ãã¦ã¯ Base64Spec.hs ã¨ãããã¡ã¤ã«ã«ãã¹ããè¨è¿°ãã¾ãã
ããã§æ³¨æãå¿ è¦ãªã®ã¯ããã¹ãç¨ã®ãã¡ã¤ã«ã¯ãã½ã¼ã¹ã¨ã¯ç°ãªããã£ã¬ã¯ããªã«ç½®ãã¹ãã§ããã¨ãããã¨ã§ããããããã¨ãå¾ã§èª¬æãã Cabal ã§ããã¹ããä¾åããããã±ã¼ã¸ã¨ãã¦èªåèªèº«ãæ¸ããããã«ãªãã¾ãã
æå³ãåãããªã人ã¯ãã¨ã«ãã "test" ã¨ãããã£ã¬ã¯ããªãä½ã£ã¦ããã¹ãé¢ä¿ã®ãã¡ã¤ã«ã¯ããã«ç½®ãã®ã ã¨è¦ãã¦ä¸ããã
"test/Base64Spec" ã®è¨è¿°ä¾ã以ä¸ã«ç¤ºãã¾ãã
spec :: Spec spec = do describe "encode" $ do it "encodes multiples of 3" $ encode "no-padding!!" `shouldBe` "bm8tcGFkZGluZyEh" it "encodes multiples of 3 + 1" $ encode "padding 2" `shouldBe` "cGFkZGluZyAgMg==" it "encodes multiples of 3 + 2" $ encode "padding1" `shouldBe` "cGFkZGluZzE=" describe "decode" $ do it "decodes no padding" $ decode "bm8tcGFkZGluZyEh" `shouldBe` "no-padding!!" it "dncodes one padding" $ decode "cGFkZGluZyAgMg==" `shouldBe` "padding 2" it "encodes two paddings" $ decode "cGFkZGluZzE=" `shouldBe` "padding1"
ãã®ããã« hspec ã§ã¯ãshouldBe ãªã©åããæãåèªã使ã£ã¦ããã¹ãã±ã¼ã¹ã楽ããæ¸ãã¾ãããã®ã楽ãããã¨ããæè¦ãã¨ã¦ã大äºã§ãã
ä¸è¨ã®ä¾ã¯ç´ç²ã§ããããã¡ãã IO ãæ¸ãã¾ãã詳ãã使ãæ¹ã¯ hspec ã®ããã¥ã¢ã«ãèªãã§ä¸ãããshouldBeãshouldReturnãshouldThrow ã¯è¦ãã§ãã¯ã§ãã
Spec ã¯ãhspec é¢æ°ã§å®è¡ã§ãã¾ãã
% ghci test/Base64Spec.hs > hspec spec encode - encodes multiples of 3 - encodes multiples of 3 + 1 - encodes multiples of 3 + 2 decode - decodes no padding - dncodes one padding - encodes two paddings Finished in 0.0956 seconds 6 examples, 0 failures
QuickCheck
hspec ã«ã¯ QuickCheck ã®æ§è³ªãã¹ããè¨è¿°ã§ãã¾ããit ã prop ã«å¤ããã° OK ã§ãã
spec :: Spec spec = do describe "encode" $ do ... prop "reverses decoded string" $ \(Base64 xs) -> encode (decode xs) == xs describe "decode" $ do ... prop "reverses encoded string" $ \xs -> decode (encode xs) == xs
Cabal
ãã¹ããèªåå®è¡ããã«ã¯ãCabal ã使ãã¾ããæºåã¨ãã¦ããã¹ãã®ããã®æ å ±ã Cabal ãã¡ã¤ã«ã«æ¸ãå¿ è¦ãããã¾ãã
Test-Suite doctest Type: exitcode-stdio-1.0 Default-Language: Haskell2010 HS-Source-Dirs: test Ghc-Options: -threaded -Wall Main-Is: doctests.hs Build-Depends: base , doctest >= 0.9.3 Test-Suite spec Type: exitcode-stdio-1.0 Default-Language: Haskell2010 Hs-Source-Dirs: test Ghc-Options: -Wall Main-Is: Spec.hs Other-Modules: Base64Spec Build-Depends: base , hspec >= 1.3 , QuickCheck , unit-test-example
hspec ã®ã¨ããã§ãä¾åããããã±ã¼ã¸ã«èªåèªèº«(unit-test-example)ãæ¸ãã¦ããã®ãåããã¾ããï¼ ãã£ã¬ã¯ããªãåããªãã¨ããã®ããã±ã¼ã¸ãä¾åããã©ã¤ãã©ãªãç¹°ãè¿ãæ¸ãã¯ãã«ãªãã¾ããã
doctest ã§ã¯ãããã«å ãã¦ä»¥ä¸ã®ãããªå 容ã "test/doctest.hs" ãã¡ã¤ã«ã«ç¨æãã¾ãã
module Main where import Test.DocTest main :: IO () main = doctest ["Codec/Base64.hs"]
doctest é¢æ°ã®å¼æ°ã¯ãdoctest ã³ãã³ãã«æ¸¡ããå¼æ°ã Haskell ã®ãªã¹ã表è¨ã«ç´ãããã®ã§ãã
hspec ã§ã¯ã以ä¸ã®ä¸è¡ã "test/Spec.hs" ãã¡ã¤ã«ã«æ¸ãè¾¼ã¿ã¾ãã
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
Cabal ã§èªåçã«ãã¹ãããæé ã¯ä»¥ä¸ã®éãã§ãã
% cabal configure --enable-tests % cabal build % cabal test Running 2 test suites... Test suite spec: RUNNING... Test suite spec: PASS Test suite logged to: dist/test/unit-test-example-0.0.0-spec.log Test suite doctest: RUNNING... Test suite doctest: PASS Test suite logged to: dist/test/unit-test-example-0.0.0-doctest.log 2 of 2 test suites (2 of 2 test cases) passed.
失æããã¨ãã¯ã表示ããã¦ãããã°ãã¡ã¤ã«ãè¦ã¦ã失æããã±ã¼ã¹ã¨ãã®ã¨ãã®å¤ãæ¢ãã¦ä¸ããã
Hspec ã®ããã©ã«ãã§ã¯ QuickCheck ã®æ§è³ªãã¹ãã§ä¹±æ°ã100åçæããã¾ãããããå¤æ´ããã«ã¯ã以ä¸ã®ããã«ãã¾ãã
% cabal test --test-option=--maximum-generated-tests=1000
Travis CI
Travis CI ã¯ãgithub ã« push ããã¨ãèªåçã«ãã¹ããèµ°ããã¦ããããµã¼ãã¹ã§ããå©ç¨ããã«ã¯ã以ä¸ã®ãããªæé ãè¸ã¿ã¾ãã
- Travis CIã«githubã¢ã«ã¦ã³ãã§ãã°ã¤ã³ãã¾ã
- å³ä¸ã«ããèªåã®ååãã¯ãªãã¯ã㦠"Accounts" ãé¸ã³ãã¢ã«ã¦ã³ããã¼ã¸ã«è¡ãã¾ã
- "Sync now" ãã¿ã³ãã¯ãªãã¯ããgithub ã®ããã±ã¼ã¸(ãªãã¸ããª)æ å ±ãåã£ã¦ãã¾ã
- Travis CI ã¨é£æºããããããã±ã¼ã¸ãé¸ã㧠On ã«ãã¾ã
次㫠Travis CI ã¨é£æºããããããã±ã¼ã¸ã®ããããã£ã¬ã¯ããªã§ã
以ä¸ã®ä¸è¡ã ".travis.yml" ã¨ãããã¡ã¤ã«ã«æ¸ãè¾¼ã¿ãgithub 㸠push ãã¾ãã
language: haskell
ããã§ããã®ããã±ã¼ã¸ã« push ãããã¨ã«ãTravis CI ä¸ã§ãã¹ããèµ°ããçµæãã¡ã¼ã«ã§éç¥ããã¾ãã
éå¤ãªãã¨
doctest 㨠Mac
doctest 㯠GHCi ãå©ç¨ãã¦å®è£ ããã¦ãã¾ããæ®å¿µãªãããMac ä¸ã§ã¯ GHCi ãä¸å®å®ã§ãããã®ããã§ãã¹ãã®å®è¡èªä½ã失æãããã¨ãããã¾ãã以ä¸ã« Mac ã¦ã¼ã¶ã¼ã¨ãã¦ã®åã®çµé¨ãæ¸ãã¦ããã¾ãã
- doctestã³ãã³ãããã cabal build ãã¦ã³ã³ãã¤ã«ããæ¹ãå®å®ãã¦ã
- GHC ã¯ã64ãããçããã32ãããçã®æ¹ãå®å®ãã¦ãã
- GHC 7.4.1 ããã GHC 7.6.1 ã®æ¹ãå®å®ãã¦ãã
å®å ¨ãªæ¹ãçµã¿åããã¦ã使ãä¸ããã
doctest ã®å¼æ°
doctest ã®å¼æ°ã¯ãGHCi ã®å¼æ°ã¨ã¾ã£ããåãã§ããdoctest ããã¾ãåããã«ã¯ããããããªå¼æ°ãå¿ è¦ã«ãªããã¨ãããã¾ããããããä¾ã¨ãã¦ã¯ã"-XOverloadedStrings" ãæãããã¾ãã
èªåã§æ¸ããCã®ã³ã¼ãããFFI ã§ãªã³ã¯ãã¦ããå ´åã¯ãæå®ãã¹ãå¼æ°ãåãããªãã¦æ³£ãããã«ãªãããããã¾ããããã®å ´åãunix-time ãã¾ãã«ããããä¾ãªã®ã§ãåèã«ããã¨ããã§ãããã
doctest 㨠haddock 㨠QuickCheck
ãã§ã« haddock ã«ã¯ "prop>" ãã¼ã¯ã¢ãããç¨æããã¦ãããQuickCheck ã®æ§è³ªãæ¸ããããã«ãªã£ã¦ãã¾ããã¾ããdoctest ãããã«å¯¾å¿ãã¦ãã¾ããåé¡ã¯ãHaskell Platform ã« "prop>" 対å¿ã® haddock ããã¤å ¥ããã§ãã
å®ã¯ãhaddock 㯠GHC ã¨ã¨ãã«é å¸ããã¦ãã¾ããGHC 㯠haddock の "master" ブランチãæ©æ¢°çã«å©ç¨ãã¾ãã"prop>" ãå®è£ ããã¦ãã haddock ã®ãã©ã³ãã¯ã"ghc-7.6" ã§ããã§ãã®ã§ã"ghc-7.6" ã "master" ã¸ãã¼ã¸ããªãã¨ããã¾ãããç¾å¨ãããã¤ãã®ãã¹ããéããªãã®ã§ãã¼ã¸ããã¦ãã¾ããããè¿ãå°æ¥ãã¼ã¸ãããã§ãããã
æå¾ã«
I wish you a merry Christmas and happy unit-testing!