Guile-QuickCheck
This Guile library provides tools for randomized, property-based testing. It follows closely the QuickCheck library written in Haskell, with inspiration from the Racket version. You can use it to define a property (a predicate with specifications for its inputs) and test it by generating many random inputs and seeing if it holds.
Example
Let’s say that we want to write a procedure that ensures we have a tasty
list of pizza toppings. We will call it ensure-tasty, and it will
save us from having to eat merely mediocre pizza. First, we need to
define what tasty pizza is! It’s a good thing that absolutely
everyone agrees about pizza toppings, so we just use the universally
acknowledged rule for tasty pizza (taken from RFC 91334):
A tasty pizza MUST have pineapples and MUST NOT have tomato slices.
In Scheme, we can define a predicate for testing if a list of toppings is tasty or not:
(define (tasty? toppings)
(and (memq 'pineapples toppings)
(not (memq 'tomato-slices toppings))))Now, before we define ensure-tasty, we will write a property that it
must satisfy using the Guile-QuickCheck tools for specifying inputs.
The property we want says “given any list of toppings as input,
ensure-tasty returns a tasty list of toppings”. There are two things
here that we need translate into to Scheme code. First we need to
specify what “any list of toppings” means, and then we need to explain
how to check if ensure-tasty returns tasty toppings.
Specifying inputs
Guile-QuickCheck provides a few combinators for specifying inputs.
These combinators all begin with “$” (which can be read as “arbitrary”).
We will define a topping as any one of a list of symbols. To define a
single constant symbol (like 'olives), we use the $const combinator.
Then, we can use the $choose combinator to specify that we want to use
one of many specifications. With $choose, each specification has to
be paired with a predicate, so the specification ends up rather verbose:
(define $topping
($choose
((cut eq? <> 'mushrooms) ($const 'mushrooms))
((cut eq? <> 'olives) ($const 'olives))
((cut eq? <> 'peppers) ($const 'peppers))
((cut eq? <> 'onions) ($const 'onions))
((cut eq? <> 'tomato-slices) ($const 'tomato-slices))
((cut eq? <> 'pineapples) ($const 'pineapples))))With $topping in place, we can use the $list combinator provided by
Guile-QuickCheck to construct a list of toppings:
(define $toppings ($list $topping))Specifying properties
Now that we have $toppings to stand in for “any list of toppings,” we
can finish writing our property:
(define ensure-tasty-property
(property ((toppings $toppings))
(tasty? (ensure-tasty toppings))))The property syntax is provided by Guile-QuickCheck, and it specifies
how to generate random inputs and how to test them. In this case we
specify that toppings should be drawn from $toppings (“any list of
toppings”). Just like let, the property syntax allows binding
multiple names, but we only need one here. We then say that if we apply
ensure-tasty to toppings, the result should satisfy tasty?.
Testing
At this point, we can write a definition for ensure-tasty and test it
against ensure-tasty-property. Let’s start with a bad definition and
see if Guile-QuickCheck can catch it:
(define (ensure-tasty toppings)
(cons 'pineapples toppings))We can test our property using the quickcheck procedure:
(quickcheck ensure-tasty-property)What does Guile-QuickCheck say?
Falsifiable after 4 tests.
Seed: 2086549941
toppings = (tomato-slices mushrooms onions)This means that after running four tests, Guile-QuickCheck found a
counter example for our property. It says that if it applies
ensure-tasty to the toppings (tomato-slices mushrooms onions), the
result is not tasty. The other thing it tells us is that the seed used
to generate the inputs is 2086549941. This could be used to generate
the same inputs again.
Let’s see if we can fix our definition of ensure-tasty:
(define (ensure-tasty toppings)
(filter (lambda (topping)
(not (eq? topping 'tomato-slices)))
(cons 'pineapples toppings)))Running quickcheck again, we see that Guile-QuickCheck says:
OK, passed 100 tests.Hooray! Guile-QuickCheck thought up 100 lists of pizza toppings, and
ensure-tasty made every single one of them tasty. Now that you’ve
learned Guile-QuickCheck, you deserve a pizza. Just make sure that it’s
tasty!
Releases
| Version | Date | Download | Signature |
|---|---|---|---|
| 0.1.0 | 2021-03-01 | guile-quickcheck-0.1.0.tar.gz | GPG Sig. |
Development
The main Git repository for this project is at https://git.ngyro.com/guile-quickcheck.