Search code examples
unit-testinghaskelltasty

Haskell Tasty Unit Tests - how to use groups and simple fixtures to avoid repetition?


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
    ]
  ]

Solution

  • 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
        ]
      ]