Scheme/HtDP Unit Testing Functions

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))

;; 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)

(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))]))

;; 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")

Studying HtDP

I haven’t posted in a while, but I’ve recently been studying How to Design Programs (HtDP). It’s really a book about teaching beginners how to program by following a series of recipes that tell you how to identify and solve different classes of problems. The techniques are taught in a very basic subset of Scheme. (There are actually multiple learning languages that slowly build up the available functionality.) You don’t have to know any Scheme to get started though.

Basic Scheme

I’m sure you can figure out what the following does without knowing any scheme:

(+ 1 3)
(* 10 10)

They explain a bit about equality and conditionals:

(= 1 1) ; true
(and true true) ; true
(or true false) ; false
(and (>= 1 1) (= 2 2)) ; true

Then building on basic arithmetic and boolean logic, they add a bit of algebra to teach how to define your own methods:

(define (add1 n)
(+ n 1))
(define (sqr n)
(* n n))

;; Tests
(= (add1 2) 3)
(= (sqr 10) 100)

It grows from those simple constructs into how to build structured and list data and how to process those kinds of data.

Why Might You Care?

It’s very hard for me to judge what it would be like to learn to program using this book. I personally have 10 years of programming experience myself and learned first with BASIC, then really with Pascal and C/C++. Professionally I first programmed in Java, then C#. From there I really got interested in dynamic languages like Python then Ruby and looked a bit at Objective-C in there. By and large these are all Imperative Programming Languages. I’m sure my experience isn’t unique for those of us who didn’t go to MIT. So, if like me, you are new to Functional Programming then it is an interesting book because it teaches you how to think in terms of functional decomposition.

Multi-core processing is the future and functional programming, due to the ability to much more easily create side-effect free programs, is well suited for parallelization. Understanding functional programming is going to be very important for programmers in the future. We might never go to all functional programming, but we might go to more mixed-mode languages that allow us to easily write parallelized code for portions of a program that need it.