Skip to content

Instantly share code, notes, and snippets.

@steckel
Created February 21, 2012 21:39
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save steckel/1879146 to your computer and use it in GitHub Desktop.
Save steckel/1879146 to your computer and use it in GitHub Desktop.
Workflow: Behavior Driven Development/Design for Front End Development (JavaScript)

Beyond any other definition, Behavior Driven Development/Design ("BDD") is a philosophy.

What is "BDD"?

"BDD" is a set of tools and techniques that exist to support one idea: good software is written to meet the needs of the stakeholders (typically clients).

These stakeholders know their business better than the people designing the software. It is the responsibility of developers to capture the business requirements and translate them into software.

BDD tools facilitate this as a process by letting developers author features first, which capture requirements in a domain-specific language. Code is developed by working on these high-level features, and then on code specifications.

Consequently, BDD is also referred to as outside-in development, and the code that is produced exists only to meet an established business need.

Why "BDD"?

Pending

Project Setup

Tools we'll be using

  • Cucumber-js: Plain english/Gherkin style of writing and testing Features
  • Gherkin: A business readable language which describes software’s features and their behavior without detailing how that behavior is implemented.
  • Jasmine: BDD style JavaScript testing framework (Synchronous)
  • Mocha: BDD/TDD/etc... testing framework (Synchronous/Asynchronous)
  • should.js: JavaScript assertions for BDD testing in and out of the browser
  • Zombie.js: Browser simulation for NodeJS.

Project Structure

  Project
  |-- features/
  |      |-- myFeature.feature
  |      |-- step_definitions/
  |      |         |-- myStepDefinitions.js
  |      |-- support
  |             |-- world.js
  |-- npm_modules/
  |      |-- cucumber/...
  |      |-- zombie/...
  |-- package.json

Example package.json

:::javascript
{
    "name": "bdd-javascript"
  , "version": "0.0.1"
  , "private": true
  , "dependencies": {
        "cucumber": ">= 0.0.1"
      , "zombie": ">= 0.0.1"
  }
}

Installing and running dependencies

From the root directory of your project (next to package.json), execute NPM

$ npm install

This will install cucumber-js and zombie.js.

The two programs will be installed into the directory node_modiles, which is where we can execute them from the command line.

ie $ ./node_modules/cucumber/bin/cucumber-js

Features

While it is usually the unspoken rule for developers to convert a User Story, Design, or conversation into a set of Features to then develop; in BDD, it is encouraged for non-developers to also be included on authoring Features for consistency and clarity between stakeholders. In a perfect world, a client could be responsible for either authoring or "signing off on" a Feature so that it can be used as a contract or ruler to measure a deliverable against.

We will be using Cucumber-js for authoring features. Cucumber is a BDD tool known for it's plain english/Gherkin syntax and evolved out of the popular Ruby BDD tool RSpec. It has implementations in for numerous programming languages but, is most well known for it's, original, Ruby implementation.

Authoring Features

Authoring features allows for both developers and non-tech stakeholders, to capture and agree on requirements before any code is written.

Using Cucumber, we will be authoring these features in Gherkin syntax which is, essentially, a simple user story.

The Gherkin "Template":

  Feature: Title
  As a "role"
  I want "feature"
  So that "benefit"

    Scenario 1: Title
      Given "context"
      And "some more context"
      When  "event"
      Then  "outcome"
        And "another outcome"

An Example Feature

An example feature written in Gherkin for the Audi redesign project may look like this:

The Gherkin "Template":

  Feature: Search Field Enabled Headline
  As a user
  I want to use a web page headline as a search field
  So that I have quick access to a site search

  Scenario: Viewing a vehicle page
    Given I am on the "A6" vehicle page
    When I click on the headline
      And type in a search phrase
      And submit my search
    Then I should see search results

Once a feature is authored and agreed upon by those involved, any non-developer can relax knowing that the developers in charge of creating and implementing the feature has enough understanding to continue on their work and begin writing code.

It is important to notice, however, that times where authoring a feature feels incomplete or difficult to author is an indicator that there has not been enough definition given. This would be due to a lack of definition and/or clarity to the authors of the feature, which, consequentially would mean a developer has too little information to begin writing any code.

Testing a Feature

Since BDD's predecessor is TDD or "Test Driven Development," testing is still a primary focus of the BDD workflow. Cucumber's main focus is to take the Gherkin Features and turn them into testable lines of code.

An example feature for testing

Here is an example feature that explains how providing a "README.md" where you can find "Usage" information, and hosting the project on GitHub will be a deliverable for project cucumber-js.

  Feature: Example feature
  As a user of cucumber.js
  I want to have documentation on cucumber
  So that I can concentrate on building awesome applications

  Scenario: Reading documentation
    Given I am on the "http://github.com/cucumber/cucumber-js" github page
    When I click on the "README.md" file link
    Then I should see "Usage"

When we run this feature against cucumber, it gives us a JavaScript representation... which brings us to...

Feature Steps

By running cucumber from the project's root directory, we'll be handed a JavaScript representation of our Features for us to complete.

$ ./node_modules/cucumber/bin/cucumber.js

:::js
this.Given(/^I am on the "([^"]*)" github page$/, function(arg1, callback) {
  // express the regexp above with the code you wish you had
  callback.pending();
});

this.When(/^I click on the "([^"]*)" file link$/, function(arg1, callback) {
  // express the regexp above with the code you wish you had
  callback.pending();
});

this.Then(/^I should see "([^"]*)"$/, function(arg1, callback) {
  // express the regexp above with the code you wish you had
  callback.pending();
});

At this point, we have a skeleton for what a Feature test can look like but, we have no logic to tell if each step in the Scenario given would pass or fail. It is the developer's job to do this.

While we can test using Cucumber in either the browser or the terminal with node.js, the simplest way at first would be to use the terminal window. We do this by creating a faux-browser using Zombie.js.

Zombie.JS

Zombie.js is a headless browser running on Node.JS. It allows us to simulate the browser experience using nothing but Node.JS via the command line. This is extremely helpful when rapid protytping or any other development scenario where testing in the browser may not be needed (quite yet).

We'll be using Zombie.JS to setup our faux-browser environment within a file named, simply, world.js. This file is considered a support file. It contains environment code that's necissary in order to test our features but, is not part of our feature's implementation code in any respect (i.e. data stubbing, etc..).

:::js
// features/support/world.js

var zombie = require('zombie');
var World = function(callback) {
  this.browser = new zombie.Browser(); // this.browser will be available in step definitions

  this.visit = function(url, callback) {
    this.browser.visit(url, callback);
  };

  callback(this); // tell Cucumber we're finished and what to use as World (this)
};

exports.World = World;

Step Definitions

Now that we have our Feature Steps "skeleton" and our support file(s) setup, we can begin to define the steps themselves.

Steps are steps that Cucumber will go through in order to test that the feature has been properely "ran through." This is not an implementation test necissarily but, a test to see if the feature is usable as it was described with the Gherkin features.

With the features we wrote, we'll want to write steps that will allow for the browser to go to "http://github.com/cucumber/cucumber-js", click on "README.md", and see "Usage".

:::js
// features/step_definitions/myStepDefinitions.js

var myStepDefinitionsWrapper;

myStepDefinitionsWrapper = function() {
  this.World = require("../support/world.js").World;

  this.Given(/^I am on the "([^"]*)" github page$/, function(arg1, callback) {
    this.visit("http://github.com/cucumber/cucumber-js", callback);
  });

  this.When(/^I click on the "([^"]*)" file link$/, function(arg1, callback) {
    this.browser.clickLink(arg1, callback);
  });

  this.Then(/^I should see "([^"]*)"$/, function(arg1, callback) {
    var regExp;
    regExp = new RegExp(arg1, "gi");
    if (!this.browser.html().match(regExp)) {
      callback.fail(new Error("Expected to see " + arg1));
    } else {
      callback();
    }
  });
};

module.exports = myStepDefinitionsWrapper;

Testing

Now that you're Features have steps defined to test againts, it's time to run the test. This is done with the same command that was used to get the step definition skeleton to begin with.

$ ./node_modules/cucumber/bin/cucumber.js

If all goes according to plan you will probably see something like this:

  ...

  1 scenario (1 passed)
  3 steps (3 passed)
@davecarter
Copy link

Great job!
Just wondering why don't u add an npm script so you can type npm run test to run cucumber..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment