HUnitを使ってみた(ついでにHLintも)
HaskellのユニットテストフレームワークであるHUnitを使ってみました。テスト対象のコードは、先日書いたsign関数です。
HUnitのインストール
なにはともあれ、まずはHUnitをインストールしました。Cabalのおかげで簡単に入れられました。
$ cabal install HUnit
テスト対象コードのモジュール名検討
つづいて、テスト対象コードのモジュール名を何にするか検討しました。このモジュールは「キープリスト」というWebアプリケーションで使う予定であり、AmazonのAPIに関わるものなので、「KeepList.Amazon」に決めました。
ファイル構成の検討
つづいて、テスト対象コードとテストコードをどのように配置するか検討しました。モジュール名の名前空間の階層をそのまま生かし、テストコードを「tests」ディレクトリに入れることに決めました。
$ tree . |-- KeepList | `-- Amazon.hs `-- tests `-- KeepList `-- Amazon.hs
テスト対象コードの記述
つづいて、テスト対象コード(KeepList/Amazon.hs)を記述しました。といっても、先日書いたコードのモジュール名を「KeepList.Amazon」に変え、main関数を削除し、sign関数の名前を「signature」に変えただけです。
module KeepList.Amazon (signature) where import Data.ByteString.Lazy.Char8 (pack) import Data.Digest.Pure.SHA (hmacSha256, bytestringDigest) import Data.ByteString.Lazy (unpack) import Codec.Binary.Base64 (encode) signature :: String -> String -> String signature secret_key message = encode $ unpack $ bytestringDigest $ hmacSha256 (pack secret_key) (pack message)
テストコードの記述
つづいて、テストコード(tests/KeepList/Amazon.hs)を記述しました。署名の例はAmazon Product Advertising APIのドキュメントから集めました。テストの書き方は「第16回 Haskellでのテストの自動化を考える(2ページ目) | 日経 xTECH(クロステック)」を参考にしました。
module Main where import Test.HUnit import KeepList.Amazon secret_key = "1234567890" tests = test [ "TuM6E5L9u/uNqOX09ET03BXVmHLVFfJIna5cxXuHxiU=" ~=? signature secret_key ("GET\necs.amazonaws.co.uk\n/onca/xml\nA" ++ "WSAccessKeyId=00000000000000000000&Act" ++ "or=Johnny%20Depp&AssociateTag=mytag-20" ++ "&Operation=ItemSearch&ResponseGroup=It" ++ "emAttributes%2COffers%2CImages%2CRevie" ++ "ws%2CVariations&SearchIndex=DVD&Servic" ++ "e=AWSECommerceService&Sort=salesrank&T" ++ "imestamp=2009-01-01T12%3A00%3A00Z&Vers" ++ "ion=2009-01-01"), "cF3UtjbJb1+xDh387C/EmS1BCtS/Z01taykBCGemvUU=" ~=? signature secret_key ("GET\necs.amazonaws.com\n/onca/xml\nAWS" ++ "AccessKeyId=00000000000000000000&Assoc" ++ "iateTag=mytag-20&Item.1.OfferListingId" ++ "=j8ejq9wxDfSYWf2OCp6XQGDsVrWhl08GSQ9m5" ++ "j%2Be8MS449BN1XGUC3DfU5Zw4nt%2FFBt87cs" ++ "pLow1QXzfvZpvzg%3D%3D&Item.1.Quantity=" ++ "3&Operation=CartCreate&Service=AWSECom" ++ "merceService&Timestamp=2009-01-01T12%3" ++ "A00%3A00Z&Version=2009-01-01"), "aMFgBNKPrz9PRR9Ato7yanlaG/PkQsNxIWYbLD1V9Zc=" ~=? signature secret_key ("GET\necs.amazonaws.jp\n/onca/xml\nAWSA" ++ "ccessKeyId=00000000000000000000&Associ" ++ "ateTag=mytag-20&ListType=WishList&Name" ++ "=wu&Operation=ListSearch&Service=AWSEC" ++ "ommerceService&Timestamp=2009-01-01T12" ++ "%3A00%3A00Z&Version=2009-01-01"), "H5u4W10g0vmyB1KA6hmkrea36AFvSryL9SQfPejvWNs=" ~=? signature secret_key ("GET\necs.amazonaws.com\n/onca/xml\nAWS" ++ "AccessKeyId=00000000000000000000&Assoc" ++ "iateTag=mytag-20&ListId=34AN6HPUN5AMX&" ++ "ListType=WishList&Operation=ListLookup" ++ "&ResponseGroup=ListItems%2COffers%2CIm" ++ "ages&Service=AWSECommerceService&Times" ++ "tamp=2009-01-01T12%3A00%3A00Z&Version=" ++ "2009-01-01"), "kEXxAIqhh6eBhLhrVMz2gt3ocMaH/OBVPbjvc9TG8ao=" ~=? signature secret_key ("GET\necs.amazonaws.com\n/onca/xml\nAWS" ++ "AccessKeyId=00000000000000000000&Assoc" ++ "iateTag=mytag-20&BrowseNodeId=465600&O" ++ "peration=BrowseNodeLookup&ResponseGrou" ++ "p=BrowseNodeInfo%2CTopSellers%2CNewRel" ++ "eases%2CMostWishedFor%2CMostGifted&Ser" ++ "vice=AWSECommerceService&Timestamp=200" ++ "9-01-01T12%3A00%3A00Z&Version=2009-01-" ++ "01"), "I2pbqxuS/mZK6Apwz0oLBxJn2wDL5n4kFQhgYWgLM7I=" ~=? signature secret_key ("GET\necs.amazonaws.com\n/onca/xml\nAWS" ++ "AccessKeyId=00000000000000000000&Assoc" ++ "iateTag=mytag-20&Condition=New&ItemId=" ++ "B0011ZK6PC%2CB000NK8EWI&Merchant=Amazo" ++ "n&Operation=SimilarityLookup&ResponseG" ++ "roup=Offers%2CItemAttributes&Service=A" ++ "WSECommerceService&SimilarityType=Inte" ++ "rsection&Timestamp=2009-01-01T12%3A00%" ++ "3A00Z&Version=2009-01-01") ]
テストの実行
以上で、テストが実行できるようになりました。実行すると、下記の通り、6ケースとも成功しました。
$ ghci GHCi, version 6.12.2: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Loading package ffi-1.0 ... linking ... done. Prelude> :load tests/KeepList/Amazon.hs [1 of 2] Compiling KeepList.Amazon ( KeepList/Amazon.hs, interpreted ) [2 of 2] Compiling Main ( tests/KeepList/Amazon.hs, interpreted ) Ok, modules loaded: Main, KeepList.Amazon. *Main> runTestTT tests Loading package HUnit-1.2.2.1 ... linking ... done. Loading package array-0.3.0.0 ... linking ... done. Loading package syb-0.1.0.2 ... linking ... done. Loading package base-3.0.3.2 ... linking ... done. Loading package bytestring-0.9.1.6 ... linking ... done. Loading package containers-0.3.0.0 ... linking ... done. Loading package binary-0.5.0.2 ... linking ... done. Loading package SHA-1.4.1.1 ... linking ... done. Loading package dataenc-0.13.0.2 ... linking ... done. Cases: 6 Tried: 6 Errors: 0 Failures: 0 Counts {cases = 6, tried = 6, errors = 0, failures = 0} *Main> :q Leaving GHCi.
HLintによるコードチェック
テストは通ったのですが、いまひとつコードの書き方に自信がないので、「How to write a Haskell program - HaskellWiki」で知ったHLintでコードをチェックすることにしました。
HLintのインストールは下記の手順でできました。
$ cabal install happy $ cabal install haskell-src-exts $ cabal install hlint
コードをチェックした結果が下記のものです。
$ hlint KeepList/Amazon.hs No suggestions $ hlint tests/KeepList/Amazon.hs tests/KeepList/Amazon.hs:6:1: Warning: Use camelCase Found: secret_key = ... Why not: secretKey = ... 1 suggestion
テスト対象コードは問題なしでしたが、テストコードには一つ問題が見つかりました。『変数名の「secret_key」だけどさ、キャメルケースの「secretKey」にしたら?』ということですね。
コードは割愛しますが、テストコードを修正し、無事「No suggestions」となりました。じつにありがたいツールです。
今後の展望
今回はテスト対象コードが一つだけだったので、HUnitの「runTestTT」でテストを走らせましたが、数が増えてきたら、この方法では手間がかかって仕方ありません。
「第16回 Haskellでのテストの自動化を考える(4ページ目) | 日経 xTECH(クロステック)」で紹介されているような、Cabalによる複数テスト実行が必要になると思っています。