Todd Wolfson

Software Engineer

May 27, 2013

I recently spoke at UtahJS and presented on the history of BDD and some of my experiments.

The presentation can be found here with the source code on GitHub. It covers the invention of TDD, evolution into BDD, the various BDD flavors, and all JS testing options currently available.

Experiments

Skeleton / Crossbones

GitHub: https://github.com/Ensighten/crossbones

For a while, I have been experimenting with BDD. At first, I wanted a cross-framework test runner from vows to mocha. This was initially called Skeleton, later renamed to Crossbones and was successfully implemented in the Sauron test suite.

This was great but I wanted something better. Crossbones required writing the tests in their own framework which was similar to vows but not vanilla vows (exporting was weird).

// Create a new test suite
var suite = new Skeleton('Sauron.js');

// Add in a test batch -- this is for a global mediator
suite.addBatch({
  'Sauron': {
    'can emit events': function () {
      Sauron.voice('hello');
    },
    'can set up functions to subscribe to events': function () {
      var works = false;
      Sauron.on('basicOn', function () {
        works = true;
      });
      Sauron.voice('basicOn');
      assert(works);
    },
    'can unsubscribe functions from events': function () {
      var count = 0;
      function basicOff() {
        count += 1;
      }

      Sauron.on('basicOff', basicOff);
      Sauron.voice('basicOff');
      Sauron.off('basicOff', basicOff);
      Sauron.voice('basicOff');
      assert(count === 1);
    }
  }
});

// Export the suite to mocha
suite.exportTo('Mocha');

// and run it
var runner = mocha.run();

Sculptor

GitHub: https://github.com/twolfson/sculptor

A long while after Crossbones and its ultimate neglect, I thought about writing a framework for cross-compiling test suites. The key reason here was to flatten test suites into TDD-compatible flavors for [testling][testling].

I began hacking on this and got quite far (it works with wrapping vows into mocha tests) but soon realized that compiling a test suite into another framework's test suite was a total mindfuck.

doubleshot

GitHub: https://github.com/twolfson/doubleshot

After considering Sculptor as an over-engineered project in attempt to find a holy grail.

I have begun to realize all of my "holy grails" are usually over-engineered items that I throw away a month later.

I decided to start on something much simpler and finer focus. I realized that saying describe versus typing out topic was a silly semantics issue. On previous projects, I wrote out a specification for my test before writing the tests themselves.

I realized this was awesome and always had the side thought that the specification would become lost once I fill out the tests due to code bloat. I wanted a cleaner way in the same loose language to keep them around.

Upon this next iteration, I tried out splitting the outline from the content and fell in love with the latest format. doubleshot was born.

// outline.json
{
  "One": {
    "is equal to one": true
  }
};

// test/content.js
{
  'One': function () {
    this.one = 1;
  },
  'is equal to one': function () {
    assert.strictEqual(this.one, 1);
  }
}

// Runs test as
describe('One', function () {
  before(function () {
    this.one = 1;
  });

  it('is equal to one', function () {
    assert.strictEqual(this.one, 1);
  });
});

Shortly after writing, I discovered cool functionality I could build in (e.g. aliasing and chaining of methods).

// outline.json
{
  "One plus two": {
    "is equal to three": true
  }
}

// content.js
{
  // Breaks 'One plus two' action into 2 actions
  'One plus two': ['One', 'plus two'],
  'One': function () {
    this.sum = 1;
  },
  'plus two': function () {
    this.sum += 2;
  },
  // Alias 'is equal to three' as 'equals three'
  'is equal to three': 'equals three',
  'equals three': function () {
    assert.strictEqual(this.sum, 3);
  }
}

// Runs test as
describe('One plus two', function () {
  before(function () {
    // These are contained inside functions but have the same effect
    this.sum = 1;
    this.sum += 2;
  });

  it('is equal to three', function () {
    assert.strictEqual(this.sum, 3);
  });
});

I am quite satisfied with the current result. There are a few kinks to work out (e.g. JSON is unordered and can cause issues). I might try out yamlish (a subset with only objects and arrays), a properietary nested interface, or even markdown.

yamlish (same ordering issues as JSON)

A strawberry:
  when washed:
    - is red
    - is tasty

Proprietary line-delimited markup

A strawberry
  when washed
    is red
    is tasty

Markdown, ordered since it compiles to XML

# A strawberry
## when washed
  - is red
  - is tasty
# A strawberry
## when washed
### is red
### is tasty

Related articles

BDD pipe dreams

Below are my most recent pipe dreams for BDD. While the example is based on testing a UI, it is applicable to anything as does BDD.

Top articles

Lessons of a startup engineer

Lessons from being a 3x first engineer, former Uber engineer, and working at even more startups

Develop faster

Removing the tedium from creating, developing, and publishing repos.