At 4:30 PM -0700 8/5/12, Matthew wrote:
On Sun, Aug 5, 2012 at 12:32 AM, Henk-Jan van Tuyl <[email protected]> wrote:
 On Sun, 05 Aug 2012 03:21:39 +0200, Matthew <[email protected]> wrote:

 I've got a function which takes in two chars, describing a playing
 card and a suit. An example would be 4C or TH for a 4 of Clubs or a
 Ten of Hearts. I need to be able to compare the ranks of a card (e.g.
 a King is 13), so a Card is a tuple of rank and suit. The function
 which parses a Card is type String -> Maybe Card.

 I'm writing unit tests for this using HUnit, and ideally I'd go with a
 table-driven[1] approach, where each test case is a tuple of the input
 and the expected output. (Possibly I could expand this to a triple, or
 simply a list, to allow for an error message for each test case.) Then
 all the test function has to do is run through each case and assert as
 necessary. Example: [("TH", Just (Hearts, 10)), ("XH", Nothing)].


 A simple solution:

 parseCard :: String -> Maybe Card
 parseCard string = <your function to test>
 test :: Bool
 test =  all testEqual [("TH", Just (Hearts, 10)), ("XH", Nothing)]
     where
       testEqual (input, output) = parseCard input == output


 For a description of 'all', see:

http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:all

Thanks for the response. The one problem I have with this is that it
will not be at all obvious which test case (or cases!) failed.

That said, maybe I could do something similar, with a Writer? A passed
test writes "", but a failed one writes a test-specific failure
message. Then the test itself uses this string as the assert message.


Let HUnit tell you about the failing test cases.  Here's one way to do it.


import Test.HUnit
import Data.Char (isDigit)

data Suit = Spades | Hearts | Diamonds | Clubs
  deriving (Show, Read, Eq, Ord)
type Rank = Int  -- 2 .. 14 (jack=11, queen=12, king=13, ace=14)
type Card = (Suit, Rank)

parseCard :: String -> Maybe Card
parseCard [rankChar, suitChar] = do suit <- suitFrom suitChar; rank <- rankFrom rankChar; return (suit, rank)
parseCard _ = Nothing

suitFrom char = lookup char [('S', Spades), ('H', Hearts), ('D', Diamonds), ('C', Clubs)]

rankFrom dig | isDigit dig = let v = read [dig] in if v >= 2 then Just v else Nothing rankFrom char = lookup char [('T', 10), ('J', 11), ('Q', 12), ('K', 13), ('A', 14)]

makeTest :: (String, Maybe Card) -> Test
makeTest (string, result) = string ~: result ~=? parseCard string

tests = [("TH", Just (Hearts, 10)), ("XH", Nothing)]

main = (runTestTT . TestList . map makeTest) tests


Dean

_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to