A property based testing library for D. Forked from dashcheck. It features a customizable tester and diverse generators and meta-generators. Bellow is a simple documentation based on examples. The full documentation as rendered with Ddoc can be found here
Generators are used to generate random values of a certain type. This is how you can use some of the generators in D.
Will return a random element of type T
.
generate!bool; // will generate a random boolean
generate!string; // will generate a random string
generate!(int[]); // will use the meta-generator list!int
generate!(immutable int[char[immutable bool]]);
// will use the meta-generator dict!(immutable int, char[immutable bool])
generate!int(-100); // generate a random integer in [-100,int.max]
generate!float(10.037f, 100f); // generate a random float in [10.037, 100]
Will return a random element form the array provided
choose([1, -1, 8, -8, 23]);
choose(["D", "C", "Python"]);
To easily recreate a set of test values there is a function setGeneratorSeed
that allows you to provide the seed to the random number generator.
setGeneratorSeed(); // the seed will be unpredictable
// ... testing code here
setGeneratorSeed(100); // the seed is now 100, same values will be generated on each run
// ... testing code here
Meta-generators are generators that take as arguments generators. They are used to construct more complex generators.
Takes at least 2 generators and returns whatever a randomly selected generator returns.
oneOf(generate!float(-1f,1f), generate!float(99f,100f)); // random number in [-1,1]U[99,100]
oneOf(2,4,0,5,10); // a random number in {2, 4, 0, 5, 10}, equivalent to choose([2,4,0,5,10])
A map function that operates on generators to construct other generators.
mapGenerate!(a => a % 2 == 0 ? a + 1 : a)(generate!int); // random odd int
mapGenerate!(a => a % 2 == 0 ? a : a + 1)(generate!int(-100,100)) // random even int between -100 and 100
mapGenerate!(a => a % 10, int) // generate a number between -10 and 10
Produce an array that has at most length N
whose elements are from the provided
generator.
list!int // generate a list of integers
list!(bool,4) // a list of at most 4 booleans
list!int(0) // generate a list of all 0s
list!(float, 3)(generate!float(3, 5)) // a list of at most 3 floats between 3 and 5
Produce an associative array whose keys are generated from the keys
generator
and values are from the keys
generator.
dict!(int, int) // a dictionary whose keys and values are random integers
dict!(int, bool) // [false: 2841782, true: -194927830]
dict!(int, bool, 10000000)(3) // [false: 3, true: 3]
Sampling is very similar to the list
meta-generator. But it's usually used
for illustration purposes and not as a generator. As well it generates a list
of exactly N
elements as opposed to one that has at most N
elements.
sample!int // a list of 10 integers
sample!(bool, 2) // a list of 2 booleans
sample(generate!float(-1f,1f)) // a list of 10 floats between -1 and 1
sample!(float, 3)(generate!float(-1f,1f)) // a list of 3 floats between -1 and 1
DamnStat forAll(alias property, const(int) n=NUM_TESTS, alias reporter=null, Generators...)(lazy Generators generators) if (isCallable!property && is(ReturnType!property == bool))
Start a property-based test.
/+ Example without reporting +/
bool idempotentSort(int[] list) {
return list.sort == list.sort.sort;
}
auto stat1 = forAll!property(generate!(int[]));
auto stat2 = forAll!(property,1000)(generate!(ulong[]));
if(stat1.passed && stat2.passed) {
writef("***** %d tests passed *****\n", stat1.testNum + stat2.testNum);
} else {
writef("***** %d/%d tests passed *****\nError at input:\n%s",
stat1.testNumRan, stat1.testNum, stat1.failStr);
writef("***** %d/%d tests passed *****\nError at input:\n%s",
stat2.testNumRan, stat2.testNum, stat2.failStr);
}
/+ Example with reporting +/
bool expandingFloat(float a, float b, float c) {
return a * (b + c) == a * b + a * c;
}
void expandingFloatReporter(float a, float b, float c) {
writeln("Failed for: ", a, " ", b, " ", c);
writefln("%.12f * (%.12f + %.12f) = %.12f", a, b, c, a*(b+c));
writefln("%.12f * %.12f + %.12f * %.12f = %.12f", a, b, a, c, a*b+a*c));
}
auto stat = forAll!(expandingFloat, 100, expandingFloatShrinker)
(generate!float, generate!float, generate!float);