Macchiato.js

Qualitative Driven Development for JavaScript

Fork me on GitHub

CAUTION

This project is under development now.
Sorry to keep you waiting.

Please see GitHub Milestones

What's "Macchiato.js" ?

Inspired by QuickCheck

Macchiato is a testing framework for JavaScript, inspired by QuickCheck, a similar library for Haskell programs.

QuickCheck is a library for random testing of program properties.

The programmer provides a specification of the program, in the form of properties which functions should satisfy, and QuickCheck then tests that the properties hold in a large number of randomly generated cases.

Specifications are expressed in Haskell, using combinators defined in the QuickCheck library. QuickCheck provides combinators to define properties, observe the distribution of test data, and define test data generators.

from QuickCheck module description

Demo

macchiato.stock({
  'number x, y => x + y == y + x':
    arbitrary( 'number', 'number' ).property( function( x, y ){
        return x + y === y + x;
  }),
  'string str => str can be regarded as boolean that is empty or not':
    arbitrary( 'string' ).property( function( str ){
        if ( str.length === 0 ){
          return false === !!str;
        }
        return true === !!str;
  }),
  'integer x, y (x !== y) => x - y !== y - x' :
    arbitrary( 'integer', 'integer' ).property( function( x, y ){
        return where( [ x !== y ], function(){
          return x - y !== y - x;
        });
  })
});

Let's Try!

Usage

Register Test Suites

macchiato.stock( labeled_properties )

macchiato.stock({ // stock test properties as object
    'number x, y => x + y === y + x' : // <label>:<callback>
        arbitrary( 'number', 'number' ).property( function( x, y ){
            return x + y === y + x, // boolean
        })
    });

Run Test Suites

macchiato.taste()

macchiato.taste(); // check all stocked test suites

in default, macchiato.js generate 100 pattern arguments per test-property.

See sample Generator works

arbitrary( type ).sample( [ opt_count ] )

arbitrary( 'boolean' ).sample() // [ true, false, false, true, true ]
arbitrary( 'boolean' ).sample( 5 ); // [ false, false, true, false, true ]

// in default, return array has 10 elements
arbitrary( 'integer' ).sample(); // [ 0, 1, 1, 0, -1, 6, 6, -12, -2, 16 ]

Use adhoc new type generator

arbitrary( type ).fmap( modifier )

// Non Negative Integer Generator
var nonNegativeNumberProperty = arbitrary( 'integer' ).fmap( Math.abs ).property( function( x ){
  return x > -1;
});

Register User generator

arbitrary( new_type ).recipe( generator )

arbitrary.( 'hoge' ).recipe(
    combinator.elements( [ 'hoge', 'huga', 'piyo', 'foo', 'bar', 'baz' ] )

arbitrary.sample(); // "foo", "bar", "bar", "hoge", "piyo", "baz", "hoge", "foo", "huga", "foo"
);

arbitrary( type ).fmap( modifier ).recipeAs( new_type )

arbitrary( 'integer' ).fmap( function( n ){
    var x = Math.max( Math.abs( n ), 1 );
    return tuple( x, x % 15 ? x % 5 ? x % 3 ? '' + x : 'Fizz' : 'Buzz' : 'FizzBuzz' );
}).recipeAs( 'fizzbuzz' );

arbitrary( 'fizzbuzz' ).sample(); // (1,"1"),  (2,"2"), (1,"1"), (2,"2"), (3,"Fizz"), (4,"4"), (5,"Buzz"), (15,"FizzBuzz"), (7,"7"), (27,"Fizz")