Up to this point we have written some code, explained type classes, and done a little pattern matching. But we haven't written anything that really does anything. Until we have some IO to perform, we will have to find another way to interact with our code. This is a perfect chance to introduce Unit Testing.
If you want to review what we've done so far, here is the previous post [Show, Compare, and Filter][1]. Next post [Intro to Parsers][8].
Starting where we left off in the previous post. We created a Date
type that was an instance of type class Show
.
-- |Show Date in YYYY-MM-DD format instance Show Date where show (Date year month day) = show year ++ "-" ++ show month ++ "-" ++ show day
When we printed the Date
type in our repl things looked right...as long as we had double digit months and days. We wanted to always get four digit years, two digit months and days.
>>> let x = (Date 2017 10 20) >>> x 2017-10-20 >>> let y = (Date 2017 1 9) >>> y 2017-1-9
Our code is broken, and we only detected it by playing around in the repl. Not a good way to do development.
There are tons of Unit Testing suites out there. All with their own reasons as to why they are exactly what you need. I went with [Hspec][2] because I've used Ruby's [RSpec][3] in the past. The Hspec site lists the following features:
So it looks like Hspec supports other major unit testing suites which is great in case you need to change for some reason. Friendly DSL and automatic detection are good too.
So lets add Hspec into our Stack build. Editing todo.cabal
, under the test-suite
section we need to add a build dependency for hspec
.
test-suite todo-test type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Spec.hs build-depends: base , todo , hspec ghc-options: -Wall default-language: Haskell2010
A quick overview of this section. hs-source-dirs
is the location in the project for tests, and main-is
defines the main source file that will be executed when running tests. build-depends
defines all the packages required to build the test binary.
To add in the automatic discovery we need to make a simple modification. Replace test/Spec.hs
with the following. Yes, the file should contain just this single line.
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
Now we can just add files to the test directory and they will automatically be included in the test. The naming structure is pretty easy:
src/.hs -> test/ Spec.hs
Now that we have everything configured correctly, and our tests are auto-discovered we can add in our first test: test/TasksSpec.hs
module TasksSpec where import Tasks import Test.Hspec (Spec, describe, context, it, shouldBe) -- |Required for auto-discpvery spec :: Spec spec = describe "Task Data Types" $ do describe "Date" $ do it "Shows YYYY-MM-DD with single digit month and day" $ do show (Date 2017 2 1) `shouldBe` "2017-02-01" it "Shows YYYY-MM-DD with double digit month and day" $ do show (Date 2017 12 11) `shouldBe` "2017-12-11"
The format should be pretty simple to understand and if it doesn't yet, seeing the output might make more sense.
If you'd like to get a full understanding of how this all works check out the [Hspec Documentation][5] over at [Hackage][6]. But the simple way of looking at it you have a description of what you're going go test, and then list what it
should do.
When we run stack test
in the command line we will see Stack download, configure and build all the dependencies for Hspec. Once you've done this all new projects will use the local copy so this process will speed up.
$ stack test ... ... Tasks Task Data Types Date Shows YYYY-MM-DD with single digit month and day FAILED [1] Shows YYYY-MM-DD with double digit month and day Failures: test\TasksSpec.hs:12: 1) Tasks, Task Data Types, Date, Shows YYYY-MM-DD with single digit month and day expected: "2017-02-01" but got: "2017-2-1" Randomized with seed 1995209293 Finished in 0.0060 seconds 2 examples, 1 failure Completed 17 action(s) Test suite failure for package todohs-example-0.0.0.1 todohs-example-test: exited with: ExitFailure 1 Logs printed to console
Looking at the output we can see the tree of our test spec.
Spec -> Tasks description -> Task Data Types description -> Date it "should" -> Shows YYYY-MM-DD...
We see that one of our tests passed (double digits) while the other test
failed.
From our failure results we can compare our expected to our actual results.
test\TasksSpec.hs:12: 1) Tasks, Task Data Types, Date, Shows YYYY-MM-DD with single digit month and day expected: "2017-02-01" but got: "2017-2-1"
We need to pad single digit numbers with zero. Should be simple by adding in a helper function.
-- |Show Date in YYYY-MM-DD format instance Show Date where show (Date year month day) = show year ++ "-" ++ showDoubleDigit month ++ "-" ++ showDoubleDigit day where showDoubleDigit num = if num < 10 then "0" ++ show num else show num
Since we have our tests in place we can just run them again and see if this fixes our issue.
$ stack test ...... Tasks Task Data Types Date Shows YYYY-MM-DD with single digit month and day Shows YYYY-MM-DD with double digit month and day Finished in 0.0020 seconds 2 examples, 0 failures Completed 2 action(s).
We pass both tests!
Next step is to implement all the tests required for the other type classes we implemented for the other data types. See the [source code][4] for a large example of what that might look like.
I thought it might be useful to state that once you have your unit test suite in place, I like to continue development doing a [Test-driven Development][7] technique called "Red/Green/Refactor". Its pretty easy to understand and follow:
colored output)
test
There is nothing saying you have to follow this methodology but I find it to be useful.
A diff of all changes in this post are available [here][4].
In the next post we will get a little more complicated and start working with parsers for both text files and user input.
=> Haskell Project: Show Compare and Filter[1] | Hspec[2] | Rspec [3] | Code [4] | Hspec (Hackage)[5] | Hackage) [6] | TDD [7] | Haskell Project: Intro to Parsers [8]
$ date: 2017-05-10 15:15 $
$ tags: haskell, tutorial, hspec, testing $
-- CC-BY-SA-4.0 jecxjo 2017-05-10
=> Comments? | back This content has been proxied by September (ba2dc).Proxy Information
text/gemini