mocha-bdd

Helper functions for practising BDD with Mocha

Usage no npm install needed!

<script type="module">
  import mochaBdd from 'https://cdn.skypack.dev/mocha-bdd';
</script>

README

mocha-bdd

Build Status License: GPL v3 Dependency Status devDependency Status

This package helps you write better BDD-style tests. It exports three functions which wrap functions from Mocha to provide a more BDD-friendly interface that encourages well-structured, atomic, reusable test code. The default export is a function which, if called, adds those three functions as globals.

Installation

mocha-bdd can be installed via NPM. Since it is a library to help with testing, it should usually be installed as a development dependency, as shown:

npm install --save-dev --production mocha-bdd

mocha-bdd is designed to work with Mocha. In particular, it assumes that Mocha’s describe and it functions are accessible globally (e.g. if your tests are being run using Mocha’s command-line tool). Therefore, Mocha is listed as a peer dependency of mocha-bdd. Mocha can be installed with the following command:

npm install --save-dev --production mocha

The peer dependency listed is Mocha version ^4.0.1, which is the latest release at the time of writing, and the release which mocha-bdd has been tested against. However, mocha-bdd should work fine with earlier versions of Mocha so long as the describe and it functions (along with their .skip and .only methods) are exposed globally.

Usage

Importing the functions

Recommended: Call the default export to add Given, When, and Then functions to the global object.

import mochaBDD from 'mocha-bdd';

mochaBDD();

This should be done in your test setup file and run before any tests. For example, by running Mocha with the --require option set to that file.

Alternatively, import the individual functions in each relevant module:

import { Given, When, Then } from 'mocha-bdd';

Writing specifications

Specifications consist of a series of steps, which fall into one of three categories:

  1. Given steps are for setup.
  2. When steps are for executing the code under test.
  3. Then steps are for making assertions based on the result.

The provided Given and When functions take a description, a step definition function, and a suite body function. The Then function takes only a description and a step definition function.

Descriptions

A description should succinctly describe the step, and should cover an atomic aspect of your test/suite. Ideally they should contain any example values that will be tested against. mocha-bdd will prepend Given, When, or Then to the beginning of your description so that the output will read the same as your specifications.

Step definition functions

These should be named in line with the descriptions. They should be created using a function declaration, so that Mocha can call it with the correct context and variables can be passed successfully among test steps.

Step definition functions can extract values from the description by matching regular expressions against this.test.parent.title (in the case of Given or When steps) or this.test.title (in the case of Then steps). They can store values in the test context either on this directly, or this.test.ctx (which you may find to be a helpful alias). Values which are assumed already to have been stored in the test context by previous test steps can be accessed as well.

Step definitions should aim to perform only what is contained in the corresponding description. If you find yourself putting additional functionality in a step definition function, consider whether you require an additional test step.

Suite body functions

These can safely be written as arrow functions. Their job is simply to contain the test steps which should be nested inside the current step.

Example specification

import given from "./given";
import when from "./when";
import then from "./then";

describe('wishHappyBirthday', () => {
    Given('a language "English"', given.aLanguage, () => {
        Given('a name "Lisky"', given.aName, () => {
            When('wishHappyBirthday is called with the name and the language', when.wishHappyBirthdayIsCalledWithTheNameAndTheLanguage, () => {
                Then('it should return "Happy birthday, Lisky!"', then.itShouldReturn);
            });
        });
        Given('a name "Satoshi"', given.aName, () => {
            When('wishHappyBirthday is called with the name and the language', when.wishHappyBirthdayIsCalledWithTheNameAndTheLanguage, () => {
                Then('it should return "Happy birthday, Satoshi!"', then.itShouldReturn);
            });
        });
    });
    Given('a language "Deutsch"', given.aLanguage, () => {
        Given('a name "Lisky"', given.aName, () => {
            When('wishHappyBirthday is called with the name and the language', when.wishHappyBirthdayIsCalledWithTheNameAndTheLanguage, () => {
                Then('it should return "Herzlichen Glückwunsch zum Geburtstag, Lisky!"', then.itShouldReturn);
            });
        });
        Given('a name "Satoshi"', given.aName, () => {
            When('wishHappyBirthday is called with the name and the language', when.wishHappyBirthdayIsCalledWithTheNameAndTheLanguage, () => {
                Then('it should return "Herzlichen Glückwunsch zum Geburtstag, Satoshi!"', then.itShouldReturn);
            });
        });
    });
    Given('an unsupported language "Esperanto"', given.anUnknownLanguage, () => {
        When('wishHappyBirthday is called with the name and the language', when.wishHappyBirthdayIsCalledWithTheNameAndTheLanguage, () => {
            Then('it should throw an error "Unsupported language"', then.itShouldThrowAnError);
        });
    });
});

Running subsets of specifications

Just like Mocha’s functions, Given, When and Then all expose .only and .skip methods which result in the corresponding subset of tests being run. For example:

Given('some setup', ..., () => {
    When.skip('something happens that should be skipped', ..., () => {
        Then('a test will be skipped', ...);
        Then.only('an exclusive test will be skipped', ...);
    });
    When('something happens that should not be skipped', ..., () => {
        Then.only('an exclusive test will run', ...);
        Then('a test will not run', ...);
        Then.only('another exclusive test will run', ...);
    });
});

Authors

License

Copyright © 2017 Lisk Foundation

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.