Skip to content

Latest commit

 

History

History
303 lines (208 loc) · 8.58 KB

README.md

File metadata and controls

303 lines (208 loc) · 8.58 KB

fluent-fix npm version Known Vulnerabilities

fluent-fix

A javascript library for creating test fixtures fluently for use in testing.

The library is aiming to be as fluent and as extensible as possible, while remaining as simple as calling a function to get started.

  1. Installation
  2. Usage
  3. Generators
  4. Extensions

1. Installation

Install the library as a development dependancy.

npm install --save-dev fluent-fix

2. Usage

2.1 Simple objects

Getting up and running and building test instances is really simple.

// Create an object that describes the defaults for the test data.
let someObject = { 
    something: 5,
    thing: "Hello World",
    stuff: [ "Good Bye" ],
    noStuff: [ ]
};

// The global FluentFix module is used to create fixtures.
let fixture = FluentFix.fixture(someObject);

// Use the fixture to create instances of the object with random data.
let testObject = fixture();

2.2 Using the fluent API

You can use the fixture to set-up specific values for your test object, rather than random ones, using the fluent api.

Using the code from above:

// Create a builder instance for the fixture.
let builder = fixture.builder(); 

// Use the builder fluent api to setup a test class.
builder
    .withSomething(10)
    .withThing('Amazing Test Value');

// Now you can use this builder to create as many test objects as you like!
let fluentTestObj1 = builder.build();
let fluentTestObj2 = builder.build();

Any values that you haven't setup with a .withXXXX() method will still have been set up with the random test data as they were in the simple value.

This means that each test case should have the absolute minimum of setup code.

Ace.

2.3 Complex objects

You can also use the fixture to setup much more complex objects like this:

// Some complicated thing yo want to generate.
let complexObject = {
    something: 5,
    thing: {
        something: 'hello',
        thing: {
            something: 'world',
            thing: {
                something: 5
            }
        }
    }
};

let fixture = FluentFix.fixture(complexObject);

2.4 Fixture inception

You can even build fixtures with other fixtures. You can use functions to setup values before calling build.

let complexFixture = fixture
    .builder()
    .withThing(function () { return fixture(); })
    .build();

2.5 Default values

The evaluation of the final object is lazy. Nothing is evaluated until you call build!

If you would like the object to have fixed values you can use a tag property on the object to mark it.

let defaultObject = {
    something: 5,
    thing: {
        'fluent-fix-default': true
        something: 'hello'
    }
}

let fixture = FluentFix.fixture(defaultObject);

The value of:

fixture().thing.something === 'hello';

Will always be fixed at the value given to the fluent-fix fixture at initialisation. This can be very useful when expecting specific output data in a test.

Enhancements

The library does not currently support functions directly, unless returned as a .withXXXX(function () { return function () { return 'test value' }}).

3. Generators

Standard generators and their options values. The options are given in the code examples as the default if not specified in the options object.

If the value is undefined it is probably because that value isn't used by default and adds extra behaviour to the generator beyond just creating a random value for your tests!

Boolean

let booleanGenerator = new FluentFix.Generator.For.Boolean({ default: undefined });

Options:

  1. default: Boolean - Specify a default boolean value for the generator.

Number

let numberGenerator = new FluentFix.Generator.For.Number({
        default: undefined,
        min: Number.MIN_VALUE,
        max: Number.MAX_VALUE,
        sequential: false
    });

Options:

  1. default: Number - Specify a default number.
  2. min: Number - Specify a minimum for random value.
  3. max: Number - Specify a maximum for random value.
  4. sequential: Boolean - Specify if you would like the random generation to be pseudo-random and sequential.

String

Simple

let stringGenerator = new FluentFix.Generator.For.String(value);

Options:

  1. value: String - This generator needs an example string to work.

Variable

let stringGenerator = new FluentFix.Generator.For.String({
    default : undefined,
    min: undefined,
    max: undefined,
});

Options:

  1. default: Date - Specify a default string.
  2. min: Date - Specify a minimum length for random string value.
  3. max: Date - Specify a maximum length for random string value.

Date

let dateGenerator = new FluentFix.Generator.For.Date({
        default: undefined,
        min: undefined,
        max: undefined,
        sequential: false,
        seed: new Date()
    });

Options:

  1. default: Date - Specify a default date.
  2. min: Date - Specify a minimum for random value.
  3. max: Date - Specify a maximum for random value.
  4. sequential: Boolean - Specify if you would like the random generation to be sequential.
  5. seed: Date - Specify the start date for a sequential generator.

Array

let arrayGenerator = new FluentFix.Generator.For.Array({
        default: undefined,
        length: 10,
        depth: 1,
        type: 0
    });

Options:

  1. default: Array - Specify a default array.
  2. length: Number - The number of items for the given depth, for instance, depth 1, legth 10 will be a simple array with length 10.
  3. depth: Number - The depth of the array, used only if complicated matrices or multidimensional arrays are necessary.
  4. type: Object|Generator - Specify an object to be used, will be parsed as if by the object generator, or a generator may be specified.

Object

let objectGenerator = new FluentFix.Generator.Object({ });

The object generator is used internally for the main parsing of objects and other generators. This shouldn't be needed for most use cases but is included here for completeness.

All unmatched properties on test fixtures are parsed finally through the object generator.

4. Extensions

What follows are the supported extension points for the library.

Base Generator

let baseGenerator = new FluentFix.Generator.Abstract(); 

// Using ES6
class YourCustomES6Generator extends FluentFix.Generator.Abstract {

    constructor () { }

    generate () {
        return 'YOUR_SPECIAL_VALUE'
    }

    static match (something) {
        return something.some_value === 'YOUR_SPECIAL_CASE';
    }
}

// Using ES5
function YourCustomES5Generator() {
    FluentFix.Generator.Abstract.call(this);
}

YourCustomES5Generator.prototype = Object.create(FluentFix.Generator.Abstract.prototype);
YourCustomES5Generator.prototype.constructor = YourCustomES5Generator;

YourCustomES5Generator.prototype.generate = function() {
    return 'YOUR_SPECIAL_VALUE'
}

YourCustomES5Generator.match = function() {
    return something.some_value === 'YOUR_SPECIAL_CASE';
}

The base generator that all other derive from in this Abstract one. You will need to implement this interface if you wish to create your own custom generators. Once you have done this you will need to add it to the current fixture context.

// Add the generators.
FluentFix.Generator.add(YourCustomES6Generator);
FluentFix.Generator.add(YourCustomES5Generator);

// Remove the generators.
FluentFix.Generator.remove(YourCustomES6Generator);
FluentFix.Generator.remove(YourCustomES5Generator);

NOTE: The matching algorithm for generators moves from most primitive to least primitive internally.

But has no intelligence regarding custom generators. This means that the ordering in which you add the generators is important. The first generator that matches the input property will be assigned for the fixture builder.

I would suggest any user of the library keeps a fixed set of generators for any scope of tests that need them to ensure that the tests are easy to reason about.