Article Image
read

TL;DR

you can use (>>):

(1 `shouldBe` 1) >> ( 1 `shouldBe` 3)

or (in a more idiomatic way) you can use do:

it "counterEx 1" $ do
  1 `shouldBe` 2
  1 `shouldBe` 1

I started to know (and, frankly, to appreciate) behavior-driven development when I started to write Scala code.

My favourite testing library in Scala is specs2. Using the words of its creator: > One thing which sets specs2 apart from other test libraries is its data model. At its heart it is a stream of “fragments” where a Fragment is a “description” and an “execution” which can return a “result”.

My typical specs2 test will look like this:

class HelloWorldSpec extends Specification { def is = s2"""

  This is a specification for the 'Hello world' string

  The 'Hello world' string should
    contain 11 characters                             $e1
    start with 'Hello'                                $e2
    end with 'world'                                  $e3
                                                      """
  def e1 = "Hello world" must haveSize(11)
  def e2 = "Hello world" must startWith("Hello")
  def e3 = "Hello world" must endWith("world")

However, in time, I started to appreciate the value of combining test results together, like this:

class HelloWorldSpec extends Specification { def is = s2"""

  This is a specification for the 'Hello world' string

  The 'Hello world' string should
    contain 11 greeting characters                      $e1
                                                        """
  def e1 = ("Hello world" must haveSize(11)).and("Hello world" must startWith("Hello"))

So it only seems natural that, while coding in Haskell, I would find myself trying to apply the same pattern.

In particular, I want to combine test results in Hspec just like specs2 (test1.and(test2)).

However, I couldn’t find any specific function for that in Hspec’s API.

Haskell does have an and operator, but it has a totally different meaning.

We could combine the test results in the most naive way, turning everything into Bools and then “combining” them with &&:

it "Bad" $ 
  (1 == 1) && ( 1 == 22 )

but the result we’d get would be:

Bad FAILED [1]
...
Failures:
  1) Ch15.Ch15, Combination test, Bad

The error message tells us that a failure occurred, but it doesn’t even tell us what the error is. Yuk!

Let’s dig a bit deeper into the library.

Hspec’s shouldBe returns an Expectation.

Expectation is a type alias of HUnit’s Assertion.

Assertion is a type alias of IO ().

IO has an instance of Monad.

Of all functions exposed by Monad, the best fit to me seems (>>), whose documentation says:

(>>) :: forall a b. m a -> m b -> m

Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such as the semicolon) in imperative languages.

If we try it out in Hspec:

it "Good" $ 
  (1 `shouldBe` 1) >> ( 1 `shouldBe` 1)
it "counterEx 1" $ 
  (1 `shouldBe` 2) >> ( 1 `shouldBe` 1)
it "counterEx 2" $ 
  (1 `shouldBe` 1) >> ( 1 `shouldBe` 3)

We get this result:

    Good
    counterEx 1 FAILED [2]
    counterEx 2 FAILED [3]
    ...
  1) Ch15.Ch15, Combination test, counterEx 1
       expected: 2
        but got: 1

  ...

  test/Ch15/Ch15Spec.hs:138:29: 
  2) Ch15.Ch15, Combination test, counterEx 2
       expected: 3
        but got: 1

Exactly what we want!

But there’s more! Since we now know that we are dealing with IO and Monad, we can sugar things up a bit and use Haskell’s do notation:

it "Good" $ do
  1 `shouldBe` 1
  1 `shouldBe` 1
it "counterEx 1" $ do
  1 `shouldBe` 2
  1 `shouldBe` 1
it "counterEx 2" $ do
  1 `shouldBe` 1
  1 `shouldBe` 3

The result, of course, it the same:

    Good
    counterEx 1 FAILED [2]
    counterEx 2 FAILED [3]
    ...
  1) Ch15.Ch15, Combination test, counterEx 1
       expected: 2
        but got: 1

  ...

  test/Ch15/Ch15Spec.hs:138:29: 
  2) Ch15.Ch15, Combination test, counterEx 2
       expected: 3
        but got: 1

In summary, we don’t need something like my revered and operator for combining tests; the Haskell ecosystem already provides (>>) and do notation.

Blog Logo

Vincibean


Published

Proudly published with Hakyll
Image

Vincibean

Just a Bunch of Tips

Back to Overview