{"id":8241,"date":"2020-06-12T22:26:34","date_gmt":"2020-06-12T22:26:34","guid":{"rendered":"http:\/\/putridparrot.com\/blog\/?p=8241"},"modified":"2020-06-12T22:27:33","modified_gmt":"2020-06-12T22:27:33","slug":"unit-testing-haskel-code-with-hunit","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/unit-testing-haskel-code-with-hunit\/","title":{"rendered":"Unit testing Haskel code with HUnit"},"content":{"rendered":"<p>As with most languages nowadays, we want to have some unit testing libraries\/frameworks. Obviously Haskell is no different and we have tools such as HUnit.<\/p>\n<p>Let&#8217;s first create a simple little bit of code to test, here&#8217;s my Calculator.hs file (abridged just to show the code I intend to write tests for)<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nmodule Modules.Calculator where\r\n\r\nfactorial :: (Integral a) =&gt; a -&gt; a  \r\nfactorial 0 = 1  \r\nfactorial n = n * factorial (n - 1) \r\n<\/pre>\n<p>We&#8217;ll need to have installed the HUnit package, if you haven&#8217;t already then run<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ncabal install --lib QuickCheck HUnit\r\n<\/pre>\n<p>We&#8217;ll store our tests in a folder named <em>test<\/em> off of our root folder (the name of the folder can be altered if you prefer).<\/p>\n<p>Here&#8217;s our test code (my file is named CalculatorTests.hs)<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nmodule Main (main) where\r\n\r\nimport Test.HUnit\r\nimport System.Exit\r\n\r\nimport Modules.Calculator as Calc\r\n\r\ntestZeroCase = TestCase(assertEqual &quot;Factorial 1&quot; (1) (Calc.factorial 1))\r\ntestNonZeroCase = TestCase(assertEqual &quot;Factorial 10&quot; (2) (Calc.factorial 10))\r\n\r\nmain :: IO ()\r\nmain = do\r\n    counts &lt;- runTestTT ( test &#x5B;\r\n        testZeroCase,\r\n        testNonZeroCase\r\n        ])\r\n    if (errors counts + failures counts == 0)\r\n        then exitSuccess\r\n        else exitFailure\r\n<\/pre>\n<p>In the above code we import our library code and have two tests, the first tests our factorial code with a 0 value, the second tests a non-zero. In this instance I&#8217;ve purposefully written a breaking test. <\/p>\n<p>Before we run these and\/or fix the tests let&#8217;s see what we&#8217;re doing  with the test code. We create a function for the TestCase and within the TestCase we have our assertion along with a message prefix (the string), next we have the expected value and finally the actual value or in this case the function call which returns the actual value.<\/p>\n<p>In <em>main<\/em> we basically create the test runner and supply an array of the test functions to be run, finally we exit the runner with success or failure.<\/p>\n<p>Now before we can run our tests we need to declare a Test-Suite within the .cabal file. We add the following to the file<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nTest-Suite test-Calculator\r\n  type:                exitcode-stdio-1.0\r\n  hs-source-dirs:      test\r\n                       .\r\n  default-language:    Haskell2010\r\n  main-is:             CalculatorTests.hs\r\n  other-modules:       Modules.Calculator\r\n  build-depends:       base &gt;=4.14 &amp;&amp; &lt;4.15, HUnit\r\n<\/pre>\n<p>This tells cabal the folder to look for the tests (<\/em>test<\/em> in this case) along with . for the current folder (otherwise our tests won&#8217;t find out Calculator module). We then state what the main application for the cabal to run for the tests, include other modules etc.<\/p>\n<p>Now run <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ncabal test\r\n<\/pre>\n<p>If all goes well you&#8217;ll get something like the following<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nRunning 1 test suites...\r\nTest suite test-Calculator: RUNNING...\r\n### Failure in: 1\r\ntest\\CalculatorTests.hs:9\r\nFactorial 10\r\nexpected: 2\r\n but got: 3628800\r\nCases: 2  Tried: 2  Errors: 0  Failures: 1\r\nTest suite test-Calculator: FAIL\r\nTest suite logged to:\r\nD:\\Development\\somefolder\\test\\HaskellBasics-0.1.0.0-test-HaskellBasics.log\r\n0 of 1 test suites (0 of 1 test cases) passed.\r\ncabal.exe: Tests failed for test:test-Calculator from\r\nCalculator-0.1.0.0.\r\n<\/pre>\n<p>Obviously the test with the prefix message &#8220;Factorial 10&#8221; failed with the expected value 2 but the actual value 3628800, so we can fix that test case so it looks like this<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ntestNonZeroCase = TestCase(assertEqual &quot;Factorial 10&quot; (3628800) (Calc.factorial 10))\r\n<\/pre>\n<p>and now all our tests will pass.<\/p>\n<p>I&#8217;ll leave it to the reader to look into the other assertions etc. For example, checkout <a href=\"http:\/\/hackage.haskell.org\/package\/HUnit-1.6.0.0\/docs\/Test-HUnit-Base.html\" rel=\"noopener noreferrer\" target=\"_blank\">Test.HUnit.Base<\/a><\/p>\n<p><strong>Other References<\/strong><\/p>\n<p><a href=\"https:\/\/hackage.haskell.org\/package\/HUnit-1.6.0.0#readme\" rel=\"noopener noreferrer\" target=\"_blank\">HUnit: A unit testing framework for Haskell<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>As with most languages nowadays, we want to have some unit testing libraries\/frameworks. Obviously Haskell is no different and we have tools such as HUnit. Let&#8217;s first create a simple little bit of code to test, here&#8217;s my Calculator.hs file (abridged just to show the code I intend to write tests for) module Modules.Calculator where [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[295,185],"tags":[],"class_list":["post-8241","post","type-post","status-publish","format-standard","hentry","category-haskell","category-unit-testing"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/8241","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=8241"}],"version-history":[{"count":4,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/8241\/revisions"}],"predecessor-version":[{"id":8245,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/8241\/revisions\/8245"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=8241"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=8241"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=8241"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}