I am taking my first steps with exception handling in Racket and would like to write a unit test, which checks, if a procedure raises an exception for a specific input.
The following is what I already have:
The procedure, which shall raise the exception:
(define (div-interval x y)
;; EXERCISE 2.10
; exception handling
(with-handlers
([exn:fail:contract:divide-by-zero?
(lambda (exn)
(displayln (exn-message exn))
#f)])
(if (or
(>= (upper-bound y) 0)
(<= (lower-bound y) 0))
(raise
(make-exn:fail:contract:divide-by-zero
"possible division by zero, interval spans zero"
(current-continuation-marks)))
(mul-interval
x
(make-interval
(/ 1.0 (upper-bound y))
(/ 1.0 (lower-bound y)))))))
The unit test:
(require rackunit)
(define exercise-test
(test-suite
"exercise test suite"
#:before (lambda () (begin (display "before")(newline)))
#:after (lambda () (begin (display "after")(newline)))
(test-case
"checking interval including zero"
(check-exn
exn:fail:contract:divide-by-zero?
(div-interval
(make-interval 1.0 2.0)
(make-interval -3.0 2.0))
"Exception: interval spans zero, possible division by zero")))
(run-test exercise-test)
There are some more tests in this test suite, but they are for other procedures, so I did not include them in this code. When I run my program, I get the following output:
before
possible division by zero, interval spans zero
after
'(#<test-success> #<test-success> #<test-success> #<test-success> #<test-error>)
Where the <test-error>
is for the test case in this post.
It seems the procedure does not raise the exception.
Is this, because I have a handler
in my procedure, which returns #f
and thus already "ate" the exception?
How would I usually write a unit test for raised exceptions?
You are absolutely right that you the code did not raise the exception because the with-handler
caught it and returned #f
. If you want the with-handler
to re-raise the exception for you you need to use raise
, making your code look something like:
(define (div-interval x y)
(with-handlers
([exn:fail:contract:divide-by-zeor?
(lambda (e)
<do-stuff>
(raise e))])
<function-body>)
Now your handler will still run, but at the end it will re-raise the exception for you.
As for testing it, you are absolutely right that check-exn
is the right way to go about it. Except because check-exn
is a procedure, you need to wrap your code in a thunk making it look like:
(require rackunit)
(check-exn
exn:fail:contract:divide-by-zero?
(lambda ()
(div-interval
(make-interval 1.0 2.0)
(make-interval -3.0 2.0))))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With