HUnit によるテストをしてみました。

本物のプログラマはHaskellを使う/ 第16回 Haskellでのテストの自動化を考えるを参考にHUnit によるテストをしてみました。

  • HUnitのインストール
$ cabal install HUnit
  • 与えられた身長(m),体重(kg) からBMI 指数を計算し、判定文字列を返します。シンガポールの文字列のほうが面白そうだったのでシンガポールの判定表を使います。
import Maybe
import Data.List
import Test.HUnit

{- 目的
与えられた身長(m),体重(kg) からBMI 指数を計算し、判定文字列を返します。

http://en.wikipedia.org/wiki/Body_mass_index
Singapore
Category	BMI range – kg/m2
Emaciation      14.9 or less
Underweight     from 15 to 18.4
Normal	        from 18.5 to 22.9
Overweight	from 23 to 27.5
Obese           from 27.6 to 40
Morbidly Obese  greater than 40
-}
shape:: Float -> Float  -> String
shape height mass = let bmi = bodyMassIndex height mass
                    in  bmiToString bmi table

bmiToString :: Float -> [(Float -> Bool, String)] -> String
bmiToString bmi tb = let (Just(_,str)) = find (\(f,str)-> f bmi) tb in str

table :: [(Float -> Bool, String)]
table = [((<15)                        ,"Emaciation"),
         ((\x-> x >= 15   && x < 18.5) ,"Underweight"),
         ((\x-> x >= 18.5 && x < 23)   ,"Normal"),
         ((\x-> x >= 23   && x < 27.5) ,"Overweight"),
         ((\x-> x >= 27.5 && x < 40)   ,"Obese"),
         ((> 40)                       ,"Morbidly Obese")]

{- 与えられた身長(m),体重(kg) からBMI 指数をを計算して返す。
  BMI 指数:体重を身長の2乗で割る
-}

bodyMassIndex :: Float -> Float  -> Float
bodyMassIndex height mass = mass / height^2


--  runTestTT bmiTests
bmiTests = "bodyMassIndex" ~:
           test [ bodyMassIndex 1.68 0  ~?= 0.0 ,
                  bodyMassIndex 1.68 60 ~?= 21.258505 ]

-- runTestTT bmiToStringTests
bmiToStringTests = "bmiToString" ~:
           test [ bmiToString 10   table ~?= "Emaciation",
                  bmiToString 15   table ~?= "Underweight",
                  bmiToString 18   table ~?= "Underweight",
                  bmiToString 18.5 table ~?= "Normal",
                  bmiToString 24   table ~?= "Overweight",
                  bmiToString 27.5 table ~?= "Obese",
                  bmiToString 39   table ~?= "Obese",
                  bmiToString 42   table ~?= "Morbidly Obese"]

じゃ、テストをしてみます。

> runTestTT bmiTests

Cases: 2  Tried: 0  Errors: 0  Failures: 0
Cases: 2  Tried: 1  Errors: 0  Failures: 0
                                          
Cases: 2  Tried: 2  Errors: 0  Failures: 0
Counts {cases = 2, tried = 2, errors = 0, failures = 0}

{-
 Cases    :テスト項目の数
 Tried    :実際に実行されたテストの数
 Errors   :エラーなどにより途中で終了した数
 Failures :失敗したテストの数
-}
--  runTestTT bmiTests
bmiTests = "bodyMassIndex" ~:
                                      -- 0.0 を1にして失敗するようにしてみます。
           test [ bodyMassIndex 1.68 0  ~?= 1 ,
                  bodyMassIndex 1.68 60 ~?= 21.258505 ]

> runTestTT bmiTests
Cases: 2  Tried: 0  Errors: 0  Failures: 0
                                          
### Failure in: bodyMassIndex:0
expected: 1.0
 but got: 0.0

Cases: 2  Tried: 1  Errors: 0  Failures: 1
                                          
Cases: 2  Tried: 2  Errors: 0  Failures: 1
Counts {cases = 2, tried = 2, errors = 0, failures = 1}


> runTestTT bmiToStringTests

Cases: 8  Tried: 0  Errors: 0  Failures: 0
Cases: 8  Tried: 1  Errors: 0  Failures: 0
Cases: 8  Tried: 2  Errors: 0  Failures: 0
Cases: 8  Tried: 3  Errors: 0  Failures: 0
Cases: 8  Tried: 4  Errors: 0  Failures: 0
Cases: 8  Tried: 5  Errors: 0  Failures: 0
Cases: 8  Tried: 6  Errors: 0  Failures: 0
Cases: 8  Tried: 7  Errors: 0  Failures: 0
Cases: 8  Tried: 8  Errors: 0  Failures: 0
Counts {cases = 8, tried = 8, errors = 0, failures = 0}