Function Generators

Function generators generate values that are functions, that your code can call. This is mostly useful for testing higher-order functions.

qc.function([args...], returnGenerator)

This will generate a function that accepts any number of arguments and returns a value produced by returnGenerator. It is a proper function mathematically speaking as the same arguments will produce the same return value. The args... argument is currently ignored, but you can use it to indicate what arguments should the function expect (this is in no way enforced).

🚧

Arguments are checked for equality using ===, so objects will not be equal to each other unless they point to the same underlying object.

describe('map', function() {
  it('produces a list with the same length as passed in', function() {
    expect(function(list, fn) {
      return list.length === map(list, fn).length;
    }).forAll(qc.array, qc.function(qc.any));
  });
});
describe 'map', ->
  it 'produces a list with the same length as passed in', ->
    expect (list, fn) ->
      list.length === map(list, fn).length
    .forAll qc.array, qc.function(qc.any)
describe('map', function() {
  it('produces a list with the same length as passed in', function() {
    expect((list, fn) => list.length === map(list, fn).length)
        .forAll(qc.array, qc.pureFunction(qc.any));
    // Note that TypeScript doesn't allow the reserved name `function`
    // as a property name, so we use the alias `pureFunction`.
  });
});

qc.procedure(object, [injector])

πŸ‘

Call for Feedback

This generator is provided as a beta release and feedback on it is welcome. If you use it in your codebase, please let the author know.

Generates a function that calls methods on the provided object. If you provide a class (function), it will first be instantiated with the new keyword and an array of arguments passed to the generated function. If there is a method called $final, this will be invoked only once, at the very end, and its return value will be returned. Otherwise the function will return undefined.

For example given this class:

function Animal() {
  console.log('Constructor');
}
Animal.prototype.a = function() {
  console.log('a');
};
Animal.prototype.b = function() {
  console.log('b');
};
Animal.prototype.$final = function() {
  console.log('$final');
  return 'return value';
};

var randomAnimal = qc.procedure(Animal)(size);
randomAnimal();
class Animal
  constructor: -> console.log('Constructor')
  a: -> console.log('a')
  b: -> console.log('b')
  $final: -> 
    console.log('$final')
    'return value'
    
randomAnimal = qc.procedure(Animal)(size)
randomAnimal()
class Animal {
  constructor() { console.log('Constructor') }
  a() { console.log('a') }
  b() { console.log('b') }
  $final() { 
    console.log('$final');
    return 'return value';
  }
}
let randomAnimal = qc.procedure(Animal)(size)
randomAnimal()

We would see an output something like this:

Constructor
b
b
a
b
a
a
$final

And the procedure would return 'return value'.

Injection

Each method can also receive arguments that will be random. Inspired by the way the AngularJS injector works, each method can ask for random arguments by naming its arguments in a special way. You can have any of qc's defined generators which take no arguments injected. Simply call them by name replacing dots with underscores. So qc.int will be int, qc.uint.large would be uint_large. You can optionally append a number which will be ignored. So function(int1, int2) would be called with two different ints.

You can use your own generators by adding them using the second, optional argument injector. The same rules apply: each matching key can also have an optional number appended to it.

Finally, you can opt to be explicit about injection and wrap your function in an array specifying generators explicitly. So function(int1, int2) {} would become [qc.int, qc.int, function(a, b) {}]. Or you can attach a special $inject property that holds the generators.

function Animal(args) {
  console.log('Constructor called with ', args);
}
Animal.prototype.a = function(int, string) {
  console.log('a ', int, string);
};
Animal.prototype.b = function(uint_large1, uint_large2) {
  console.log('b', uint_large1, uint_large2);
};
// $args is a special injector, it is the original arguments the function
// was called with.
Animal.prototype.$final = function($args) { 
  console.log('$final', $args);
  return 'return value';
};

var randomAnimal = qc.procedure(Animal)(size);
randomAnimal('Hello');
class Animal
  constructor: (args) -> 
    console.log('Constructor called with ', args)
  a: (int, string) -> 
    console.log('a ', int, string)
  b: (uint_large1, uint_large2) -> 
    console.log('b', uint_large1, uint_large2)
  $final: ($args) -> 
    console.log('$final', $args)
    'return value'

randomAnimal = qc.procedure(Animal)(size)
randomAnimal 'Hello'
class Animal {
  constructor(args: any[]) {
      console.log('Constructor called with ', args);
  }
  a(int: number, string: string) { 
      console.log('a ', int, string); 
  }
  b(uint_large1: number, uint_large2: number) {
      console.log('b', uint_large1, uint_large2);
  }
  $final($args: any[]) { 
    console.log('$final', $args);
    return 'return value';
  }
}
let randomAnimal = qc.procedure(Animal)(size);
randomAnimal('Hello');

Would output something like:

Constructor called with ['Hello']
a 45 ';r]etg'
a 23 'fbg345bg'
b 4235345 546923
b 2314 4325445
a 0 'JD2d32dj2d'
$final ['Hello']