Scheme/HtDP Unit Testing Functions

October 19, 2008 - 3 minute read -
functional programming Unit Testing HTDP

How to Design Programs (HtDP) provides a series of unit testing functions that allow you to test the output of any kind of function. The current version talks about testing your code, but doesn't offer a lot of guidance into how to do that. Everything you need is there though, so in case you are doing this on your own and don't have a teacher to tell you to do this, here's some explanation that I hope helps.

check-expect

check-expect takes the results of a function and the expected value. This is the simplest of the testing constructs which answers the question "Does the expected result of a function call equal the result returned by the function?". It handles all of the details about types of values returned, so it can work with a simple value like a number, a complex value like a structure or a list, or even a picture.

;; add: number number -> number
;; add two numbers together
(define (add n m)
  (+ n m))</p>
<p>;; Tests
(check-expect (add 1 1) 2)

check-within

check-within is similar to check-expect, but it allows for a third argument to tell wether or not the result is within a certain difference of the actual answer. This is good for certain functions that might produce random results, but the most common case is when dealing with floating point numbers. Floating point numbers are not always represented precisely, so there needs to be some "room for error" so to speak.

(define PI 3.14)
(check-within (/ 22 7) PI .003)</p>
<p>(check-within (random 10) 0 9)

check-error

Finally, you can use check-error to test cases where your function throws an error. Those error conditions are an important part of a function contract, so testing them should be done just like any other possible conditions.

One of the things that threw me at first was what to use as the expected value of a check-error test.

An error is HtDP is "throw" by a function like:

(error 'function-name "some message")

After a bit of trial and error, I found out the expected result is a string like:

; "function-name: some message"
(check-error (error 'list-pick "list too short")  "list-pick: list too short")

More Realistic Example

;; list-pick : list-of-symbols N[>= 0]  ->  symbol
;; to determine the nth symbol from alos, counting from 0;
;; signals an error if there is no nth item
(define (list-pick alos n)
  (cond
    [(empty? alos) (error 'list-pick "list too short")]
    [(= n 0) (first alos)]
    [(> n 0) (list-pick (rest alos) (sub1 n))]))</p>
<p>;; Tests
(check-error (list-pick empty 1)  "list-pick: list too short")
(check-expect (list-pick (list 'a 'b 'c 'd) 3) 'd)
(check-error (list-pick (list 'a 'b 'c 'd) 4) "list-pick: list too short")