Package Exports
- yadda
- yadda/lib/parsers
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (yadda) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Yadda
Yadda brings true BDD to JavaScript test frameworks such as Jasmine, Mocha, QUnit, Nodeunit and CasperJS. By true BDD we mean that the ordinary language (e.g. English) steps are mapped to code, as opposed to simply decorating it. This is important because just like comments, the decorative steps such as those used by Jasmine, Mocha and Vows, can fall out of date and are a form of duplication.
Yadda's BDD implementation is like Cucumber's in that it maps the ordinary language steps to code. Not only are the steps less likely to go stale, but they also provide a valuable abstraction layer and encourage re-use. You could of course just use CucumberJS, but we find Yadda less invasive and prefer it's flexible syntax to Gherkin's. Yadda's conflict resolution is smarter too.
Installation
Yadda 0.6.1 is the current verison, and includes a trivial update to error handling. However 0.6.0 involved a rewrite of the mocha plugin and therefore breaking API changes. See the release notes and examples for more deatils.
Node based environments (e.g. Mocha)
npm install yaddaBrowser based environments (e.g. QUnit)
<script src="./lib/yadda-0.6.1.js"></script>Writing Yadda Tests
Step 1 - Write your scenarios
bottles.feature
Feature: 100 Green Bottles
Scenario: should fall from the wall
Given 100 green bottles are standing on the wall
When 1 green bottle accidentally falls
Then there are 99 green bottles standing on the wall(You aren't restricted to just Given/When/Then. You can use any words you like)
Step 2 - Implement the step library
bottles-library.js
var assert = require('assert');
var Library = require('yadda').localisation.English;
module.exports = (function() {
var library = new Library()
.given("$NUM green bottles are standing on the wall", function(number, next) {
wall = new Wall(number);
next();
})
.when("$NUM green bottle accidentally falls", function(number, next) {
wall.fall(number);
next();
})
.then("there are $NUM green bottles standing on the wall", function(number, next) {
assert.equal(number, wall.bottles);
next();
});
})();(If your test runner & code are synchronous you can omit the calls to 'next')
Step 3 - Integrate Yadda with your testing framework (e.g. Mocha)
bottles-test.js
var Yadda = require('yadda');
Yadda.plugins.mocha();
feature('./bottles.feature', function(feature) {
var library = require('./bottles-library');
var yadda = new Yadda.Yadda(library);
scenarios(feature.scenarios, function(scenario, done) {
yadda.yadda(scenario.steps, done);
});
});Step 4 - Run your tests
mocha --reporter spec bottles-test.js
100 Green Bottles
✓ should fall from the wallFeatures
Supported Libraries
Yadda works with Mocha, Jasmine, QUnit, Nodeunit, ZombieJS and CasperJS. See the examples for details.
Flexible BDD Syntax
It's common for BDD libraries to limit syntax to precondition (given) steps, action (when) steps and assertion (then) steps. Yadda doesn't. This allows for more freedom of expression. e.g.
var library = new Yadda.Library()
.define("$NUM green bottle(?:s){0,1} standing on the wall", function(number) {
// some code
})
.define("if $NUM green bottle(?:s){0,1} should accendentally fall", function(number) {
// some code
})
.define("there are $NUM green bottle(?:s){0,1} standing on the wall", function(number) {
// some code
});
new Yadda.yadda(library).yadda([
"100 green bottles standing on the wall",
"if 1 green bottle should accidentally fall",
"there are 99 green bottles standing on the wall"
]);However we think that Given/When/Then (along with And/But/With) is a good starting point, so we recommend using Yadda.localisation.English instead of the vanilla Yadda.Library. This adds 'given', 'when', 'then', 'and', 'but' and 'with' helper methods, enabling you to define your steps as follows...
var library = new Yadda.Library()
.given("$NUM green bottle(?:s){0,1} standing on the wall", function(number) {
// some code
})
.when("$NUM green bottle(?:s){0,1} should accendentally fall", function(number) {
// some code
})
.then("there are $NUM green bottle(?:s){0,1} standing on the wall", function(number) {
// some code
});
new Yadda.yadda(library).yadda([
"Given 100 green bottles standing on the wall",
"when 1 green bottle should accidentally fall",
"then there are 99 green bottles standing on the wall"
]);We'd be delighted to accept pull requests for more languages and dialects.
Feature Descriptions
You can add an optional feature description at the top of your file to give some context about the scenarios contained within bottles-spec.txt
Feature: As a bystander
I can watch bottles falling from a wall
So that I can be mildly amused
Scenario: should fall from the wall
Given 100 green bottles are standing on the wall
When 1 green bottle accidentally falls
Then there are 99 green bottles standing on the wallThere can only be a single feature present in a file - it really doesn't make sense to have two, and you will be issued with an error if you try to include more than one
Annotations
Annotations can be added to a feature to enable you to do any kind of pre-processing required. These take the form of key/value pairs and can be added like this:
bottles-spec.txt
@browser=chrome
@theme=bottles
Feature: As a bystander
I can watch bottles falling from a wall
So that I can be mildly amused
Scenario: should fall from the wall
Given 100 green bottles are standing on the wall
When 1 green bottle accidentally falls
Then there are 99 green bottles standing on the wallStep Anatomy
A step is made up of a regular expression, a function and some context.
var ctx = { assert: assert };
library.given('^(\\d+) green bottle(?:s){0,1} standing on the wall$', function(n) {
wall = new Wall(n);
this.assert.equals(wall.bottles, n);
}, ctx);Regular Expressions
The regular expression is used to identify which steps are compatible with the input text, and to provide arguments to the function. You can specify step signatures using true RegExp object, which is handy if they contain lots of backslash characters. e.g.
var library = new Yadda.Library.English()
.given(/^(\d+) green bottle(?:s){0,1} standing on the wall$/, function(n) {
// some code
});Regular expressions can get pretty ugly, so it's often preferable to relax the regex and use a $term variable which will be replaced with a wildcard i.e. '(.+)'.
var library = new Yadda.Library.English()
.given(/$NUM green bottles standing on the wall/, function(n) {
// some code
});Using $term variables can relax the regular expression too much and cause clashes between steps. Yadda provides greater control over the expansion through use of a dictionary, e.g.
var dictionary = new Yadda.Dictionary()
.define('gender', '(male|female)')
.define('speciaility', '(cardio|elderly|gastro)');
var library = new Yadda.Library.English(dictionary)
.given('a $gender, $speciality patient called $name', function(gender, speciality, name) { /* some code */ });will expand to
"(?:[Gg]iven|[Aa]nd|[Ww]ith]|[Bb]ut) a (male|female), (cardio|elderly|gastro) patient called (.+)"and therefore match "Given a female, elderly patient called Carol". The expansions can also contain $terms so
var dictionary = new Yadda.Dictionary()
.define('address_line_1', '$number $street')
.define('number', /(\d+)/)
.define('street', /(\w+)/);
var library = new Yadda.Library.English(dictionary)
.given('a street address of $address_line_1', function(number, street) { /* some code */ });will expand to
"(?:[Gg]iven|[Aa]nd|[Ww]ith]|[Bb]ut) a street address of (\d+) (\w+)"Functions
The function is the code you want to execute for a specific line of text. If you don't specify a function then a no-op function will be used, which is Yadda's way of implementing a 'Pending' step.
Contexts (Shared State)
The context will be bound with the function before it is executed and provides a non global way to share state between steps, or pass in define time variables such as an assertion library or 'done' function. The context is also optional.
It can be a chore to add a context to every step, so a common context can be specified at the interpreter and scenario levels too...
// Shared between all scenarios
new Yadda.yadda(library, ctx);
// Shared between all steps in this scenario
new Yadda.yadda(library).yadda('Some scenario', ctx, done);If you specify multiple contexts they will be merged before executing the step.
Step Conflicts
One issue you find with BDD libraries, is that two steps might match the same input text. Usually this results in an error, and you end up having to add some extra text to one of the steps in order to differentiate it. Yadda attempts to minimise this in three ways.
By using the Levenshtein Distance to determine which step is the best match when clashes occur.
By allowing you to define steps in multiple libraries. Grouping steps into libraries not only helps keep a tidy code base, but also prevents clashes if you scenario doesn't require the library with the alternative step.
If you still have problems with clashing, you can use the term dictionary to make your regular expression more specific without affecting the readability of your step.
Events
Debugging BDD tests is typically harder than debugging unit tests, not least because you usually can't step through a feature file. You can make things a bit easier by adding event listeners, which log the step that is being executed.
var EventBus = require('Yadda').EventBus;
EventBus.instance().on(EventBus.ON_EXECUTE, function(event) {
console.log(event.name, event.data);
});
The following events are available...
| Event Name | Event Data |
|---|---|
| ON_SCENARIO | { scenario: [ '100 green bottles', 'should 1 green bottle...', ...], ctx: context } |
| ON_STEP | { step: '100 green bottles...', ctx: context } |
| ON_EXECUTE | { step: '100 green bottles...', pattern: '/(\d+) green bottles.../', args: ['100'], ctx: context } |