Cucumber, the popular Behaviour-Driven Development tool, brought to your JavaScript stack.
It runs on both Node.js and modern web browsers.
Try it now: http://cucumber.no.de!
Cucumber.js is still a work in progress. Here is its current status.
Feature | Status |
---|---|
Core (scenarios, steps, mappings) | Done |
Background | Done1 |
Calling steps from step defs | To do |
Comments | Done |
Command-line interface | Done1, 2 |
Command-line options | To do2 |
Data tables | Done |
Doc Strings | Done |
Failing steps | Done |
Hooks | Done |
I18n | To do |
JSON formatter | To do |
Pretty formatter | To do2 |
Scenario outlines and examples | To do |
Stats collector | To do |
Step argument transforms | To do |
Tags | Done |
Undefined steps | Done |
Wire protocol | To do |
World | Done |
String defined step definitions | Done |
- Not certified by Cucumber TCK yet.
- Considered for removal from Cucumber TCK.
- Simple Around, Before and After hooks are available.
Feature | Status |
---|---|
Background | Done1 |
CoffeeScript support | Done |
Command-line interface | Done |
- Will be certified by Cucumber TCK.
Cucumber.js is tested on:
- Node.js 0.4, 0.6 (see CI builds) and supposedly 0.5.
- Google Chrome
- Firefox
- Safari
- Opera
There are plans to have CI builds on browsers too.
Cucumber.js is available as an npm module.
Install globally with:
$ npm install -g cucumber
OR
You may also define cucumber.js as a development dependency of your application by including it in a package.json file.
// package.json
{ "devDependencies" : {
"cucumber": "latest"
}
}
Then install with npm install --dev
Features are written with the Gherkin syntax
# features/myFeature.feature
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 cucumber.js github page
When I go to the README file
Then I should see "Usage"
Support files let you setup the environment in which steps will be run, and define step definitions.
World is a constructor function with utility properties, destined to be used in step definitions
// 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 are the glue between features written in Gherkin and the actual SUT (system under test). They are written in JavaScript.
All step definitions will run with this
set to what is known as the World in Cucumber. It's an object exposing useful methods, helpers and variables to your step definitions. A new instance of World
is created before each scenario.
Step definitions are contained within one or more wrapper functions.
Those wrappers are run before executing the feature suite. this
is an object holding important properties like the Given()
, When()
and Then()
functions. Another notable property is World
; it contains a default World
constructor that can be either extended or replaced.
Step definitions are run when steps match their name. this
is an instance of World
.
// features/step_definitions/myStepDefinitions.js
var myStepDefinitionsWrapper = function () {
this.World = require("../support/world.js").World; // overwrite default World constructor
this.Given(/REGEXP/, function(callback) {
// Express the regexp above with the code you wish you had.
// `this` is set to a new this.World instance.
// i.e. you may use this.browser to execute the step:
this.visit('http://github.com/cucumber/cucumber-js', callback);
// The callback is passed to visit() so that when the job's finished, the next step can
// be executed by Cucumber.
});
this.When(/REGEXP/, function(callback) {
// Express the regexp above with the code you wish you had. Call callback() at the end
// of the step, or callback.pending() if the step is not yet implemented:
callback.pending();
});
this.Then(/REGEXP/, function(callback) {
// You can make steps fail by calling the `fail()` function on the callback:
if (!this.isOnPageWithTitle("Cucumber.js demo"))
callback.fail(new Error("Expected to be on 'Cucumber.js demo' page"));
else
callback();
});
};
module.exports = myStepDefinitionsWrapper;
Hooks can be used to prepare and clean the environment before and after each scenario is executed.
To run something before every scenario, use before hooks:
// features/support/hooks.js (this path is just a suggestion)
var myHooks = function () {
this.Before(function(callback) {
// Just like inside step definitions, "this" is set to a World instance.
// It's actually the same instance the current scenario step definitions
// will receive.
// Let's say we have a bunch of "maintenance" methods available on our World
// instance, we can fire some to prepare the application for the next
// scenario:
this.bootFullTextSearchServer();
this.createSomeUsers();
// Don't forget to tell Cucumber when you're done:
callback();
});
};
module.exports = myHooks;
The before hook counterpart is the after hook. It's similar in shape but is executed, well, after every scenario:
// features/support/after_hooks.js
var myAfterHooks = function () {
this.After(function(callback) {
// Again, "this" is set to the World instance the scenario just finished
// playing with.
// We can then do some cleansing:
this.emptyDatabase();
this.shutdownFullTextSearchServer();
// Release control:
callback();
});
};
module.exports = myAfterHooks;
It's also possible to combine both before and around hooks in one single definition with the help of around hooks:
// features/support/advanced_hooks.js
myAroundHooks = function() {
this.Around(function(runScenario) {
// "this" is - as always - an instance of World promised to the scenario.
// First do the "before scenario" tasks:
this.bootFullTextSearchServer();
this.createSomeUsers();
// When the "before" duty is finished, tell Cucumber to execute the scenario
// and pass a function to be called when the scenario is finished:
runScenario(function(callback) {
// Now, we can do our "after scenario" stuff:
this.emptyDatabase();
this.shutdownFullTextSearchServer();
// Tell Cucumber we're done:
callback();
});
});
};
module.exports = myAroundHooks;
Hooks can be conditionally elected for execution based on the tags of the scenario.
// features/support/hooks.js (this path is just a suggestion)
var myHooks = function () {
this.Before("@foo", "@bar,@baz", function(callback) {
// This hook will be executed before scenarios tagged with @foo and either
// @bar or @baz.
// ...
callback();
});
};
module.exports = myHooks;
Cucumber.js includes a binary file to execute the features.
If you installed cucumber.js with npm install --dev
, you may run cucumber with:
@NODE_ENV=test ./node_modules/.bin/cucumber.js
You may specify the features to run:
@NODE_ENV=test ./node_modules/.bin/cucumber.js features/myFeature.feature
And require specific step definitions with the --require option:
@NODE_ENV=test ./node_modules/.bin/cucumber.js features/myFeature.feature \
--require features/step_definitions/myStepDefinitions.js
A few example apps are available for you to browse:
- Rails app serving features in the browser
- Express.js app running features in the cli
- Try cucumber.js in the browser
Install the required dependencies:
$ npm link
$ node example/server.js
Then go to localhost:9797.
$ node_modules/.bin/jasmine-node spec
There is a common set of features shared by all cucumber implementations. It's called the Technology Compatibility Kit or TCK. Find more on the Cucumber TCK repository.
The official way of running them is through Cucumber-ruby and Aruba. Ruby and Bundler are required for this to work.
$ git submodule update --init
$ bundle
$ rm -rf doc; ARUBA_REPORT_DIR=doc cucumber features/cucumber-tck -r features
Note: you need the bcat and rdiscount gems in order to use the ARUBA_REPORT_DIR
environment variable. Install it with gem install bcat rdiscount
.
You can then open the generated documentation:
$ open doc/features/cucumber-tck/*.html # might open a lot of files ;)
In addition to that, Cucumber.js is able to run the features for itself too:
$ ./bin/cucumber.js features/cucumber-tck -r features
There are a few other Cucumber.js-dependent features. Execute everything:
$ ./bin/cucumber.js
Alternatively, you can run everything with the help of Rake:
$ git submodule update --init
$ bundle
$ rake
You can display debug messages by setting the DEBUG_LEVEL environment variable. It goes from 1
to 5
. 5
will display everything, 1
will only print out the critical things.
$ DEBUG_LEVEL=5 ./bin/cucumber.js
It even works with Aruba:
$ rm -rf doc; DEBUG_LEVEL=5 ARUBA_REPORT_DIR=doc cucumber features/cucumber-tck -r features
$ open doc/features/cucumber-tck/*.html # you'll see debug messages in Aruba-generated docs
- Twitter: @cucumber_js
- IRC: #cucumber on Freenode
- Google Groups: cukes
- cukes.info
- Update development status in
README.md
, if relevant - Update
History.md
- Bump version in
lib/cucumber.js
- Bump version in
package.json
- Add new contributors to
package.json
, if any - Commit those changes as "Release 0.1.2" (where 0.1.2 is the actual version, of course)
- Tag commit as "v0.1.2" with short description of main changes
- Push to main repo on Github
- Wait for build to go green
- Publish to NPM
- Deploy to cucumber.no.de