I'm trying to create a testing framework for my homework assignment. The idea is to pass a list of tests (each of them consists of a function, parameters and and an expected value) to a function that will do the testing and then print a symbol that represents success, failure or an error (exception raised).
The problem is that functions as well as paramters may have different types like int * int -> int
or string list -> bool
. I store them in tuples but I can't put them in a list since they have different types.
One way to do this is to create a datatype that will have a constructor for each case but it's tedious. So my question is there a simple way to do this?
One trick is to package up all the functionality you need into a functions of type unit -> unit
. Then you can put these into a list.
Let's build up a small example. I might implement a small testing framework like so:
datatype 'a result = Value of 'a | Raised of exn
type ('a, 'b) test =
{ func : 'a -> 'b
, input : 'a
, check : 'b result -> bool (* checks if the output is correct *)
}
fun runTest {func, input, check} =
let val result = Value (func input) handle e => Raised e
in if check result then
print "Correct\n"
else
case result of
Value _ => print "Incorrect\n"
| Raised e => print ("Raised " ^ exnMessage e ^ "\n")
end
Here's an example usage:
val t1 =
{ func = op^
, input = ("hello", "world")
, check = (fn Value "helloworld" => true | _ => false)
}
val t2 =
{ func = Word.fromInt
, input = 5
, check = (fn Value 0w5 => true | _ => false)
}
val _ = runTest t1
val _ = runTest t2
But now, as you point out, you have the problem that you can't do this:
val tests = [t1, t2] (* doesn't typecheck *)
val _ = List.app runTest tests
To fix this, I'd recommend doing the following.
type test' = unit -> unit
fun make (t : ('a, 'b) test) : test' =
(fn () => runTest t)
fun runTest' (t' : test') = t' ()
This approach packages up all the functionality you need from a test (that is, running it, checking that it is correct, etc.) into a function of type unit -> unit
. Now, all of your "tests" are the same type, and can be put in a list.
val t1' = make t1
val t2' = make t2
val tests' = [t1', t2']
val _ = List.app runTest' tests