You don’t need to callback cucumber-js, but it will call you… i promise

Although it’s documented on https://github.com/cucumber/cucumber-js it wasn’t clear to me what the best is to deal with callbacks in step definitions. After working with cucumber-js for a while it is now clear. So let me share my experiences with you in this area.

Basically there 3 ways of handling steps in a scenario:

  1. Use the callback given as the last parameter
  2. Return a A+ promise 
  3. None of the above

Let’s say we have the following scenario:

When you run it for the first time and have not implemented any of the steps you will get the following snippits:

As you can see the default behavior is a callback. So it is easy to implement the Give to set up the stage.

Now usually the When step involves some kind of back-end call and promises are a common way to handle this.
The great thing is that we can you return a promise as-is, but the catch here is to remove the callback from the arguments. There is some magic going on there and if you leave it in you won’t go to the next step and the whole process will keep hanging. I found myself using console.log quite a few times.

In the Then step we assert the outcome the promise by getting the state (for example). Also here we don’t need the callback, because if our assertion fails it will throw an error and your scenario failed. Otherwise it can carry on silently like the documentation indicates.

Now to be honest this made the experience of using cucumber-js a lot better. It is a very good tool to have in my daily work and hope this article will help you in that area as well.

One last thing… All of the above can also be achieved with only callbacks, however this makes the step definitions a whole lot messy-er and that’s bad when your projects grows bigger.

  • Nilesh Kulkarni

    This is great. Have you tried with a selenium test. All WebDriverJS commands return a promise. So I did something like you mention, but cucumberjs doesn’t execute When step.

    this.Given(/^I launch app$/, function () {
    return this.driver.get(this.targetBaseUrl);
    });

    this.When(/^I enter the details$/, function () {
    return accountPage(this.driver).enterDetailsToCreateAccount(); //this also returns a promise.
    });

    • richard ruiter

      Hi Nilesh,

      Yes i have used that a lot before and works.

      For example:

      /* When clauses */
      this.When(/^the monitoring dashboard is shown$/, function () {
      return this.dashboard.open();
      });