This is the third article in a series about building a JavaScript data generation library, which I’ve called Symphonize.js. This library generates data for Orchestrate, with specific configuration parameters for getting data into a key/value or graph part of a collection. It can be used for testing certain models you’ve setup, interactions that need to be stepped through, et cetera.

Part one of this series was a quick intro to key value and graph data, where I built a JSON sample data generator using chance.js. Part two covered CRUD (Create Read Update and Delete) as it’s applied to complex NoSQL solutions like a key/value store. I did this using the orchestrate.js library project available on github that @sjkaliski put together. Today, I going to dive into building the actual library by getting a good BDD/TDD process going.

Library Mission: Provide an easy to use, configurable data generation library that is available via NPM and generates Orchestrate.io focused key/value and graph based data.

One of the core missions of the library is configurability. The idea is to provide something like the below, in JSON format, so that the specific key/value, graph and data related and stored in these elements can have multiple data types stored in them. This sample is the most simple configuration needed to generate data.

{"schema":"keyvalue"}

The minimum configuration required to generate data is a JSON object with a “schema” set to “key/value.” Besides being sure mocha is installed, the first three things to setup are should.js, a hook to the Symphonize code, and a default bit of JSON to work with.

var should = require('should');
var Symphonize = require('../bin/symphonize');
var default_keyvalue_spec = {"schema":"keyvalue"};

‘Should’ is a library that fluently writes test assertions. So instead of something like this:

Assert.equal(someValue, someOthervalue);

it would actually be written like:

someValue.should.eql(someOtherValue);

This provides for a more readable way to write the test. Otherwise, everything else is the same when using mocha. My first test was to verify that Symphonize would accept the JSON configuration, and provide a response to what it received to generate. It ended up like this:

describe('the default keyvalue generation', function () {
    it('should determine that it is generating a key value schema.', function(){
        var symphonize = new Symphonize(default_keyvalue_spec);
        var result = symphonize.generating_schema();
        result.should.eql("keyvalue");
    })
})

Now I’ll use constructor invocation to accept a parameter required by the rest of the code and methods. Then to verify what the accepted parameter is, the JSON is passed in and asking for a key/value data generation. I’ve assumed the existence of a .generating_schema() function that will return the generating schema. Now, for a little implementation:

function Symphonize(generation_spec) {
    this._generation_spec = generation_spec;
}

Symphonize.prototype.generating_schema = function(){
    return this._generation_spec.schema;
}

When I run the test I get a wonderful green light. I’ll add a test to verify that the data generation actually occurs and returns something. I also added something to insure the new constructor works and returns a usable result. Here I added two more tests:

describe('the default keyvalue generation', function () {
    it('should construct through constructor given invocation.', function () {
        var symphonize = new Symphonize(default_keyvalue_spec);
        symphonize.should.exist;
    })
})

describe('the default keyvalue generation', function () {
    it('should return generated data given an empty spec.', function () {
        var symphonize = new Symphonize(default_keyvalue_spec);
        symphonize.generate().should.exist;
    })
})

These two tests verify rudimentary things are happening, but they also help in leading us in the correct direction. These tests, even if thrown away later, help insure we haven’t missed basic parts of the code. In the first test, I’ve verified symphonize does indeed exist. This is assumed since I did more than just implement the test after the first test and implement cycle. In the second test above, I’m expecting a result to be created when calling the symphonize.generate() function. At this point, I don’t even care what it returns, it just needs to return something.

Running these will get a pass on the first, on the second it will get an error. I took a stab at the test to get all green lights with the following code.

Symphonize.prototype.generate = function(){
    var keyValue = 'junk';
    return keyValue;
}

With this code I get a green light. However ‘junk’ doesn’t exactly cut it for anything useful. I needed another test to verify something useful is being produced. What was I trying to get? The answer to that would be a key/value generation of data. I figured I would create a test to check for the key and value of the return result.

describe('the default keyvalue generation', function () {
    it('should return one key value result.', function () {
        var symphonize = new Symphonize(default_keyvalue_spec);
        var keyValue = symphonize.generate();

        keyValue.key.should.exist;
        keyValue.value.should.exist;
    })
})

Now I’ve got something that verifies I’m getting something closer to what I need. Implementation time:

Symphonize.prototype.generate = function () {
    var keyValue = {
        "key": chance.guid(),
        "value": chance.paragraph({sentences: 1})
    };

    return keyValue;
}

Got a green light with this one. All good on these fronts, so it’s time to tidy up the test code and finalize the implementation code. The first thing I did was to get all of the tests put together under a category, so that when they run, they’re aligned better. My finalized test code looks like this:

var should = require('should');
var Symphonize = require('../bin/symphonize');
var default_keyvalue_spec = {"schema": "keyvalue"};

describe('the default keyvalue generation', function () {
    it('should determine that it is generating a key value schema.', function () {
        var symphonize = new Symphonize(default_keyvalue_spec);
        var result = symphonize.generating_schema();
        result.should.eql("keyvalue");
    })
    it('should construct through constructor given invocation.', function () {
        var symphonize = new Symphonize(default_keyvalue_spec);
        symphonize.should.exist;
    })
    it('should return generated data given an empty spec.', function () {
        var symphonize = new Symphonize(default_keyvalue_spec);
        symphonize.generate().should.exist;
    })
    it('should return one key value result.', function () {
        var symphonize = new Symphonize(default_keyvalue_spec);
        var keyValue = symphonize.generate();

        keyValue.key.should.exist;
        keyValue.value.should.exist;
    })
})

After tidying up the test code, I took a stab at the implementation code. I didn’t need to do to much because it was minimalistic as it exists. When I was done, my total implementation code looked like this:

var Chance = require('chance');
var chance = new Chance();

function Symphonize(generation_spec) {
    this._generation_spec = generation_spec;
}

Symphonize.prototype.generating_schema = function () {
    return this._generation_spec.schema;
}

Symphonize.prototype.generate = function () {
    var keyValue = {
        "key": chance.guid(),
        "value": chance.paragraph({sentences: 1})
    };

    return keyValue;
}

module.exports = Symphonize;

Summary

I’ve walked through a behavioral test-driven design approach to building the data generation code. In the next article, I’ll take a look at stepping through the graph store, and then we’ll move on to implementation and wrap up to start a new topic.