1

How would you go about writing a Tasty HUnit test that incorporates multiple checks against a single fixture variable in a single test, or at least a tidy group of such tests?

For example, consider this Gherkin specification:

Scenario: A 3-Vector has x, y and z components
  Given: a <- Vec(1.0, 2.0, 3.0)
  Then: a.x = 1.0
  And a.y = 2.0
  And a.z = 3.0

I could do something like this, but it's very repetitive:

unitTests = testGroup "All Unit Tests"
  [ testCase "A 3-Vector has x, y and z components" $ assertEqual [] (x $ Vec 1.0 2.0 3.0) 1.0
  , testCase "A 3-Vector has x, y and z components" $ assertEqual [] (y $ Vec 1.0 2.0 3.0) 2.0
  , testCase "A 3-Vector has x, y and z components" $ assertEqual [] (z $ Vec 1.0 2.0 3.0) 3.0
  ]

The concern I have about this is that I've repeated the scenario name three times, and I've also created the fixture three times. I'd like to find a way to group all three assertions into a single group with the title "A 3-Vector has x, y and z components", and only specify the fixture Vec once.

I could expand the test specification to minimise some of the description repetition, but I'd rather stick to the Gherkin spec if I can:

unitTests = testGroup "All Unit Tests"
  [ testCase "A 3-Vector has x component" $ assertEqual [] (x $ Vec 1.0 2.0 3.0) 1.0
  , testCase "A 3-Vector has y component" $ assertEqual [] (y $ Vec 1.0 2.0 3.0) 2.0
  , testCase "A 3-Vector has z component" $ assertEqual [] (z $ Vec 1.0 2.0 3.0) 3.0
  ]

I don't know of a way to define the Vec just once, for the group.

What I'd like to do is something like this (not real code!):

unitTests = testGroup "All Unit Tests"
  [ testScenario "A 3-Vector has x, y and z components" 
    let v = Vec 1.0 2.0 3.0 in
    [ testCase "x" assertEqual [] (x $ v) 1.0
    , testCase "y" assertEqual [] (y $ v) 2.0
    , testCase "z" assertEqual [] (z $ v) 3.0
    ]
  ]
davidA
  • 12,528
  • 9
  • 64
  • 96
  • 3
    Why is that not real code? Just rename `testScenario` to `testGroup`, add a `$` at the end of that line, and it’ll do precisely what you want. – Joachim Breitner May 08 '20 at 09:22
  • @JoachimBreitner thank you, as a Haskell beginner I'm pleasantly surprised that what I hoped was possible pretty much is. I've posted my working code as an answer. – davidA May 08 '20 at 23:37

2 Answers2

3

Thank you to Joachim Breitner who suggested that my "not real code" wasn't too far off the mark. He is correct.

With a bit of tweaking I ended up with this, which works as I want it to:

data Vec = Vec { x, y, z :: Double } deriving (Show)

unitTests = testGroup "All Unit Tests"
  [ testGroup "A 3-Vector has x, y and z components" $
    let v = Vec 1.0 2.0 3.0 in
    [ testCase "x" $ assertEqual [] (x v) 1.0
    , testCase "y" $ assertEqual [] (y v) 2.0
    , testCase "z" $ assertEqual [] (z v) 3.0
    ]
  ]
davidA
  • 12,528
  • 9
  • 64
  • 96
0

It's perfectly fine to have multiple assertions within a testCase. So you could do this:

unitTests = testGroup "All Unit Tests"
  [ testCase "A 3-Vector has x, y and z components" $ do
    let v = Vec 1.0 2.0 3.0
    assertEqual [] (x v) 1.0
    assertEqual [] (y v) 2.0
    assertEqual [] (z v) 3.0
  ]
Roman Cheplyaka
  • 37,738
  • 7
  • 72
  • 121