Unit tests, revisited

This chapter describes how together with Chez Scheme’s resident random-number generator, let-expressions make it clearer how to write unit-test procedures.

Resources

Chez Scheme’s random-number generator

Chez Scheme’s predefined procedure random, when applied to a positive integer n, returns a random non-negative integer smaller than n:

> (random 5)
4
> (random 5)
0
> (random 5)
0
> (random 5)
3
> (random 5)
4
> (random 5)
1
> (random 5)
3
>

In all cases, the results of evaluating (random 5) range between 0 and 4. In particular, they range between 0 and 1 if we evaluate (random 2), as if we were flipping a coin.

Whatever.

—popular saying

Exercise 09

Implement a generator of random lowercase characters.

Hint: Use the resident procedure char->integer and its left inverse integer->char that respectively convert a character into its ASCII code and vice versa.

Solution for Exercise 09

Bong-sun: Let me see. First, char->integer and integer->char:

> (integer->char (char->integer #\a))
#\a
> (integer->char (char->integer #\z))
#\z
>

Dana: Actually, we might as well look at the ASCII codes of #\a and of #\z:

> (char->integer #\a)
97
> (char->integer #\z)
122
>

Alfrothul: OK. So 97, 98, etc. are the ASCII codes of #\a, #\b, etc.

Bong-sun: Let me check:

> (- (char->integer #\z) (char->integer #\a))
25
>

Dana: Right. There are 26 letters of the alphabet.

Bong-sun: So if we add a random number between 0 and 24 to the ASCII code of #\a, we get the ASCII code of a lowercase character.

Alfrothul: And if we convert this ASCII code to a character, this character is lowercase:

(define random-char-lowercase
  (lambda ()
    (integer->char (+ (char->integer #\a) (random 26)))))

Pablito: Let me try, let me try:

> (random-char-lowercase)
#\i
> (random-char-lowercase)
#\x
> (random-char-lowercase)
#\f
> (random-char-lowercase)
#\b
>

Exercise 10

Implement a generator of random uppercase characters.

Application to unit testing

Bong-sun: Aha!

The fourth wall: Ahem.

Bong-sun: Oops. Sorry, Fourth Wall. Can we have an interlude now?

The fourth wall: Sure thing.

Bong-sun: Thanks.

Interlude

Bong-sun: Aha!

The fourth wall: OK, I am curious. Aha, what?

Bong-sun: Now we can more conveniently test properties in our unit tests.

The fourth wall: Pray elaborate.

Bong-sun: It’s really simple. We can name random numbers locally, and then test computations that involves these random numbers. For example, consider a candidate procedure that allegedly implements the addition function for non-negative integers. We can test whether it is commutative and associative:

(define unit-tests-for-add
  (lambda (candidate)
    (and (let ([x (random 100)]
               [y (random 100)])
           (= (candidate x y)
              (candidate y x)))
         (let ([x (random 100)]
               [y (random 100)]
               [z (random 100)])
           (= (candidate (candidate x y) z)
              (candidate x (candidate y z)))))))

Exercise 11

  1. Implement a fake addition procedure that passes Bong-sun’s unit tests and yet that does not implement the addition function.
  2. Strengthen Bong-sun’s unit tests with a few concrete tests, e.g., that applying the candidate procedure to 2 and 2 yields 4.
  3. Devise another fake addition procedure that passes the strengthened unit tests and yet that does not implement the addition function.
  4. Strengthen Bong-sun’s unit tests some more so that it defeats both the fake addition procedures in a. and in c.

Resources

Version

Created [13 Sep 2025]