README
gentest
Property-based, generative testing for JavaScript.
Don't handwrite unit tests. Save time and catch more bugs by writing properties, and let the computer generate test cases for you!
(This is a work in progress. Consider it "Stability 1: Experimental" for the time being. Feedback welcome.)
Basic example
Let's say we want to test this add function:
function add(x, y) {
return x + y;
}
We can begin by asking, "What properties should this function have?"
One property is that it's commutative; add(x, y)
should equal
add(y, x)
for any integers x and y. To test this, we could write a
function that accepts a particular pair of values for x and y, and
returns true if the property holds for those inputs:
var ourProperty = function(x, y) {
return add(x, y) === add(y, x);
};
Such a function is called a property in Gentest, but we're not
quite finished. We also need to tell Gentest what x
and y
are so
it can generate sample values. For now, let's restrict our input
domain to integers, which we can create using the gentest.types.int
generator.
var t = gentest.types;
forAll([t.int, t.int], 'addition is commutative', function(x, y) {
return add(x, y) === add(y, x);
});
We now have a complete example and can run the tests using the
gentest
executable. npm install -g gentest
, then run gentest
with your test file as an argument.
Concepts and terms
A property is a parameterized test: a function that takes any number of arguments and returns a boolean, together with a description of how to generate that function's arguments.
A test is a particular test case, that is, a set of arguments to a property.
API
gentest.sample(type, [count])
Generates sample values of the given type.
gentest.types
Contains the following type definitions, with built-in generators:
int
int.nonNegative
int.nonZero
int.positive
char
string
bool
gentest.sample(gentest.types.int);
// -> [ 0, 0, -1, 1, 0, 2, -2, 1, -2, -4 ]
gentest.sample(gentest.types.string);
// -> [ '', '', '', 'V', 'N', '{C', '(P', 'jb', 'I{=y', 'Ss' ]
And these higher-order type definitions:
arrayOf(type)
Produces arrays of the argument type.
gentest.sample(gentest.types.arrayOf(gentest.types.bool));
// ->
// [ [],
// [],
// [ false ],
// [ false ],
// [ false ],
// [ false ],
// [ false, true, true ],
// [ true, true, true ],
// [ false,
// false,
// true,
// true ],
// [] ]
tuple(types)
Produces arrays that have one each of the given types, in order.
var t = gentest.types;
gentest.sample(t.tuple([t.int, t.int, t.bool, t.string]))
// ->
// [ [ -1, -1, true, '' ],
// [ 0, 0, true, 'B' ],
// [ 2, 1, true, '!B' ],
// [ 0, 0, true, '' ],
// [ 2, 2, false, '\'D' ],
// [ 2, 2, true, '@+' ],
// [ 3, 1, true, '7gR]' ],
// [ -2, 0, true, 'Z' ],
// [ 0, -4, false, 'rr$:' ],
// [ 5, 4, true, '' ] ]
oneOf(types)
Produces any of the given types.
gentest.sample(gentest.types.oneOf([gentest.types.bool, gentest.types.int]));
// ->
// [ 0,
// true,
// 1,
// false,
// true,
// true,
// true,
// -1,
// 0,
// -4 ]
constantly(x)
Returns a generator that always yields the constant value x
.
elements(elems)
Any of the given elements.
var foods = gentest.types.elements(['pizza', 'chocolate', 'sushi']);
gentest.sample(foods);
// ->
// [ 'sushi',
// 'pizza',
// 'pizza',
// 'chocolate',
// 'sushi',
// 'pizza',
// 'chocolate',
// 'chocolate',
// 'chocolate',
// 'sushi' ]
shape(object)
Produces objects, with each key mapped to a value of the respective type.
var person = gentest.types.shape({
name: gentest.types.string,
age: gentest.types.int.positive
});
gentest.sample(person);
// ->
// [ { name: '', age: 1 },
// { name: '', age: 1 },
// { name: 'y', age: 1 },
// { name: '