Testing Prolog Programs
We explain several methods of declarative testing.
"Declarative" means that we reason about what is
being described by the program: We compare the actual
meaning of the program with the intended one, and the
actual properties with those that we know will hold
if the program is implemented correctly.
Example: Length of a list
Let us consider a simple Prolog program that contains a
mistake:
list_length([], 0).
list_length([_|Ls], N) :-
N #> 0,
N #= N0 + 2,
list_length(Ls, N0).
Our intention is to describe the relation between a list
and its length.
From a quick glance, the program at
first seems to work as intended in several ways. For example, the
query ?- list_length([], 0). succeeds as
intended, and we can use the program to generate answers:
?- list_length(Ls, _).
Ls = []
; Ls = [_A]
; Ls = [_A,_B]
; ... .
Nevertheless, the program is wrong!
Testing nontermination
Our first tests aim to ensure
desired termination
properties of the program. For example, in the case
of list_length/2, we know that the
program must not terminate for
the most general query. This is because we expect the
relation to hold for lists of all lengths, and there
is no way to report this set with a finite number of answers.
To study termination of a Prolog query Q, we use the
query:
?- Q, false.
Iff this query terminates, then we say that Q
terminates universally.
In our concrete case, we use:
?- list_length(Ls, L), false.
No termination can be observed. In this respect, the
program behaves as expected. However, testing termination
properties is not sufficient to detect all mistakes. What
else can we do?
Concrete test cases
We can try concrete test cases that we know
should succeed or fail. Prolog makes it very easy to
test pure programs in this way: You can
simply try queries on the toplevel! In addition, Prolog's built-in
backtracking mechanism makes it very easy to express a vast
collection of concrete test cases at once.
For example, as already stated, we know that the
relation should yield answers for lists
of all lengths. In particular, it should succeed for
lists of the form [], [a], [a,a],
... .
Let us
see if a counterexample exists:
?- maplist(=(a), Ls), \+ list_length(Ls, _).
None is reported. So, also in this respect, the program behaves as
intended. We can push this further and generalize the test
cases for example to:
?- maplist(=(_), Ls), \+ list_length(Ls, _).
Again, no counterexample is reported, even with this massive
generalization.
Testing against a reference implementation
What else can we do? We can for example test against
a reference implementation. In our concrete case, this is
easily possible, because almost all Prolog implementations ship
with a predicate called length/2 which is exactly
the relation we are trying to describe. Testing against the
reference implementation immediately reveals a mistake in our
program:
?- A #\= B, length(Ls, A), list_length(Ls, B).
A = 1, B = 2, Ls = [_A]
; A = 2, B = 4, Ls = [_A,_B]
; A = 3, B = 6, Ls = [_A,_B,_C]
; ... .
Of course, in most cases we do not have a reference
implementation!
Testing declarative properties
We can test the relation by using further properties that
we know must hold if the relation is implemented correctly.
For example, in our case, we know that if the length of a
list Ls is N, then the length
of [_|Ls] is N+1. Let us again try to
find a counterexample of this:
?- list_length(Ls, N), list_length([_|Ls], N1), N1 #\= N + 1.
Ls = [], N = 0, N1 = 2
; ... .
So there it is: From this solution, we know that either the
length of [] or the length of [_]
is not described correctly. In this case, it is clear that
the length of [_] should be 1, but
is incorrectly 2. In particular, the following
query incorrectly fails:
?- list_length([_], 1).
false.
Continue with Declarative
Debugging to see how we can locate and correct the
mistake.
Further reading
The approaches we describe above are by no means exhaustive. There
are several other declarative ways to test Prolog programs. One
very promising approach is
concolic testing, as explained for example by
Mesnard et al. in
Concolic
Testing in Logic Programming and the included
references.
More about Prolog
Main page