JSVerify

Write powerful and concise tests.

JSVerify

JSVerify is a property-based testing library, highly inspired by QuickCheck. It is testing framework agnostic, you could use JSVerify with Mocha, nodeunit, Jasmine or any other framework.

Property based testing

Write properties about your function that should hold true for all inputs, instead of enumerating expected inputs and outputs. Tests written this way are concise and powerful.

Property-based tests make statements about the output of your code based on the input, and these statements are verified for many different possible inputs. [1]

Property-based testing encourages a high level approach to testing in the form of abstract invariants functions should satisfy universally, with the actual test data generated for the programmer by the testing library. In this way code can be hammered with thousands of tests that would be infeasible to write by hand, often uncovering subtle corner cases that wouldn't be found otherwise. [2]

Getting Started

Install the module from npm registry with: npm install jsverify

Clone the source from GitHub and contribute!

Examples

Boolean to Boolean function applied thrice

This example is taken from older revision of Software Foundations book. We could prove the proposition by hand, as there are only four distinct bool → bool functions. Or we can let JSVerify generate inputs for us.

// forall (f: bool -> bool, b: bool), f (f (f b)) ≡ f(b).
var boolFnAppliedThrice =
  jsc.forall("bool -> bool", "bool", function (f, b) {
    return f(f(f(b))) === f(b);
  });

jsc.assert(boolFnAppliedThrice);
// OK, passed 100 tests

Sort is idempotent

A unary operation (or function) is idempotent if, whenever it is applied twice to any value, it gives the same result as if it were applied once; i.e., ƒ(ƒ(x)) ≡ ƒ(x). For example, sort: sort(sort(x)) ≡ sort(x).

// forall (f: string -> nat, arr: array string),
sortBy(sortBy(arr, f), f) ≡ sortBy(arr, f).
var sortIdempotent =
  jsc.forall("string -> nat", "array string", function (f, arr) {
    return _.isEqual(_.sortBy(_.sortBy(arr, f), f), _.sortBy(arr, f));
  });

jsc.assert(sortIdempotent);
// OK, passed 100 tests

Promises

We could test asynchronous methods with JSVerify too. Just return a Promise from a property function.

function delay(timeout, f) {
  var complete;
  var p = new Promise(function (c, r) {
    complete = c;
  });

  setTimeout(function () {
    complete(f());
  }, timeout);

  return p;
}

var noEffectOnPureComputations =
  jsc.forall("json -> json", "json", jsc.nat(100), function (f, x, t) {
    var sync = f(x);
    return delay(t, function () {
      return f(x);
    })
    .then(function (async) {
      return _.isEqual(sync, async);
    });
  });

jsc.check(noEffectOnPureComputations);
// OK, passed 100 tests

Minimal counterexamples

In case property doesn't hold, JSVerify tries to find the smallest possible counterexample.

var wrongTakeProp =
  jsc.forall("array nat", "nat", function (arr, n) {
    return _.take(arr, n).length === n;
  });

jsc.check(wrongTakeProp);
//Failed after 2 tests and 6 shrinks. rngState: ...;
//  Counterexample: []; 1;  [ [], 1 ]

Here the problem was in the property specification:

var correctTakeProp =
  jsc.forall("array nat", "nat", function (arr, n) {
    return _.take(arr, n).length <= n; // Less than or equal to!
  });

jsc.check(correctTakeProp);
// OK, passed 100 tests

Documentation

Check the README on GitHub for API and usage documentation.