0

I am implementing "smart" constructor performing runtime checking as described here https://wiki.haskell.org/Smart_constructors#Smart.28er.29_constructors

My first question is : how to unit test that invalid argument throws ?

Here is what I have tried

import Control.Exception
import Test.HUnit

metalResistor :: Bands -> Resistor
metalResistor n = Control.Exception.assert (n >= 4 && n <= 8) $ Metal n

m0 = metalResistor 0
test1 = TestCase ( assertBool  (show mo) False)
tests = TestList [TestLabel "test1" test1]

Results is

*Main> runTestTT  tests
### Error in:   0:test1
Assertion failed
CallStack (from HasCallStack):
  assert, called at test.hs:42:19 in main:Main
Cases: 1  Tried: 1  Errors: 1  Failures: 0
Counts {cases = 1, tried = 1, errors = 1, failures = 0}

My expectation is to catch the exception in the unit test (assertThrow ?) and that the test succeed.

My second question is maybe more opinion base, but I am unsure which approach to take for smart constructor : the one with error or the one with Conntrol.Exception.assert

In the long run, I feel better to use the error solution ; for sake of clarity and for code maintenance and bug tracking, plus that tedious to write one time is better that tedious to read 1000 times.

sandwood
  • 2,038
  • 20
  • 38
  • Related: https://stackoverflow.com/a/35600656/67579 – Willem Van Onsem Sep 28 '18 at 09:51
  • 2
    But personally I do not like the idea in general to raise errors. In Haskell, one usually returns a `Maybe Resistor` in that case, or `Either String Resistor`. – Willem Van Onsem Sep 28 '18 at 09:56
  • @WillemVanOnsem : yes, I was wondering if what is described is conform with the general "way of solving issue" of FP. Obviously you think it is not. – sandwood Sep 28 '18 at 09:59
  • 4
    well of course this is a bit opinion based, but the problem I have with exceptions, is that these are not explicit in the signature. That means that if you would define something like `f = metalResistor`, then if you export `f`, without documentation, I have no hint at all that `f` does that (by only looking at the implementation of `f`, and its signature). This also makes it easy to pass `f` as a generic function to all kinds of function that do not expect an exception (and thus do not handle it accordingly). – Willem Van Onsem Sep 28 '18 at 10:15
  • As @WillemVanOnsem said it is best to use Maybe or Either String. One reason, is that it makes your test easier ;-). The other it is easier to transform a function returing a Maybe to the assert version that the other way around. The last (but not the last) is you might want to parse a file which contains many resistors, and don't want to fail on the first one, but read them all and displays all the errors. You do that easily with Maybe but not by throwing exception ... unless you are planning to catch them. In that case use Maye, Exception should be exceptional (and not used as control flow) – mb14 Sep 28 '18 at 14:47
  • @mb14 : the first reason is very convincing indeed. I will rewrite my code using Either as I can always assert it after. It' s not easy for a beginner in Haskell to assess if a resource is good or not. This one is on the Haskell wiki but obviously it does not mean it should be taken for granted. – sandwood Sep 28 '18 at 19:54

0 Answers0