Migrating from CucumberJS 1.x to 2.x

12 May 2017 was D-Day, or should I say, C(uke)-Day. After a lot of release candidates CucumberJS officially released their stable version of CucumberJS 2. But what changed and how is it to migrate from version 1.x to 2? In this post I’m going to point out the biggest changes.

Example project

As an example project I will refer to this boilerplate from my previous post called from compiler to boilerplate. That boilerplate holds a default setup with

Before we start, I just want to say it costed me less than 20 minutes to migrate my Protractor + Typescript + CucumberJS from CucumberJS version 1 to 2, see the changes here. I’ve did an estimation for the customer I’m working for and I don’t expect that it will take me more than half a day (already created a large set of tests), so time shouldn’t be the breaker ;-).

Let’s start

The hardest part was the reading…, just kidding. That was the easiest part. Just start with reading the changelog of CucumberJS, you can find it here. I’ve made it easier for you so the link starts with the first release of 2.0.0-rc0.

CLI

Strict is default

Previously pending and undefined steps did not cause an exit code of 1. This could be overridden with --strict. Strict is now the default and you can use --no-strict from the command line to return to the previous behaviour.

This is not a  big change, but it can be the difference in a failing test with version 2 in comparison to version 1.

--tags

One change that I expect will cost some extra work is the change to cucumber-tag-expressions. With Tag expressions you’ll get a more descriptive way of describing which tags are (not) needed when running a test. It is no longer repeatable and new values will override previous tags. For example

  • @dev stays the same
  • ~@dev becomes 'not @dev'
  • @foo,@bar becomes '@foo or @bar'
  • @foo @bar becomes '@foo and @bar'*

 * The changelog of CucumberJS explains that @foo @bar becomes '@foo and bar'. This is not correct, bar should have a @ so it becomes  '@foo and @bar', see also here.

The reason why I expect it will cost some work is that lot of CucumberJS implementations use a taskrunner or have a custom implementation. You’ll or need to wait for the taskrunner to be upgraded to be compatible with CucumberJS 2, or adjust your own implementation.

In my current project we don’t use the tags a lot, so there’s no real change / effort. In my previous project we relied on the tags in our Jenkins pipeline, so I expect they have some work to do in their Groovy scripts and custom taskrunner implementation before migrating ;-).

attach

For me this change wasn’t a big change. Because there is no scenario-object anymore data needs to be attached to world constructor now. The code now looks like this

// 1.3.0
this.After(function(scenario, callback) {
    scenario.attach(new Buffer([137, 80, 78, 71]), 'image/png')
});

// 2.0.0
this.After(function() {
    this.attach(new Buffer([137, 80, 78, 71]), 'image/png');
});

Be aware that you can’t write it as a Lambda function because you will loose the this(world)-scope if you do so.

If you want to pass the this(world)-scope you can do it for example like this

// 2.0.0
this.After(function() {
    const world = this;
    passWorldScope(world);
});

So the code change is only to remove scenario, callback from the arguments and change scenario to this.

If you are using a reporting tool to parse the output be aware of the change that string attachments are no longer base64 encoded. Buffer and Stream attachments are still base64 encoded.

Just use multiple-cucumber-html-reporter and it’s fixed for you (encoded or not) 😉

Hooks

defineSupportCode

In my (previous) projects I always did a module.exports for the hook implementation  like this

module.exports = function (){
    // The hook implementation
};

Hooks now need to be written with the defineSupportCode method.

var {defineSupportCode} = require('cucumber');

defineSupportCode(function({After, Before}) {
    // The hook implementation
});

See also here.

scenario to scenarioResult

Hooks now receive a scenarioResult instead of the scenario. Changing this isn’t that much work, I even see this as a big improvement because with one line of code you get more detailed information about the scenario itself (Feature, scenario, step and all the details that belong to them).

I could only find one downside. That is that we can’t use scenario.isFailed() anymore. We now need to use for example scenarioResult.status === 'failed'.

scenario.isFailed() has disappeared, now use scenarioResult.status === 'failed' instead.

Event Handlers

Objects no longer have get* methods and instead have exposed properties. For example: scenario.getName() is now just scenario.name.

When I look at my boilerplate, my current customer and my previous customer the change itself doesn’t have a big impact. It can be changed within the same change when scenario is changed to scenarioResult.

tags

When you use tags on your hooks you also need to change them because

  • they should now be a string instead of an array
    this.Before({tags: ["@foo"]},function(scenario) {})

    becomes

    this.Before({tags: "@foo"}, function (scenario) {})
  • the cucumber-tag-expressions, see the --tags above.
    this.Before({tags: ["@foo", "@bar"]}, function (scenario) {})

    becomes

    this.Before({tags: "@foo or @bar"}, function (scenarioResult) {})

In my opinion this shouldn’t be a big effort.

Step definitions

In my (previous) projects I always did a module.exports for the step definitions  like this

module.exports = function (){
    // The step implementation
};

but now you need to do it like this

var {defineSupportCode} = require('cucumber');

defineSupportCode(function({Given, When, Then, After, Before}) {
    // The step implementation
});

See also here. Depending on the amount of steps-files it’s a quick and simple change.

In my opinion this change also has an advantage. You can now easily add the After- and Before-hooks in your step implementation if they have the same scope.

Only add the After and Before hooks in your step implementation if they have the same scope / belong to the same steps. If not, place them in a separate hooks file.

That’s all folks

Really? Yep. There are some more minor changes, but I haven’t seen the impact yet in my example project.

Keep in mind, a well structured (code-wise) project will be easier to adjust. As said, it costed me less than 20 minutes for my example project and it will cost me more time for my new project (but I don’t expect more than half a day).

As far as I can see it now the most effort needs to be put into projects that rely on Cucumber tags in combination with a (custom) taskmanager. So good luck with that 🙂

I hope you can use the information in your project and if you have question feel free to ask them here and please check https://github.com/wswebcreation/protractor-cucumber-typescript-boilerplate for some more details.

 

Grtz,

Wim

 

Credits:

← Previous post

Next post →

2 Comments

  1. Kyon Perez

    I’m using protractor to run my cucumberjs scripts. I have a package.json file to run it using “npm test” command.

    This was the “old” syntax that used to work, which I have in the package.json file:

    protractor ./FM_IntTest_UI_conf.js –cucumberOpts.tags ~@ignore –cucumberOpts.tags @smoke,@rt,@sprint

    Based on what the new cucumberjs doc says…
    At Old Cucumberjs This –cucumberOpts.tags ~@ignore –cucumberOpts.tags @smoke,@rt,@sprint
    In Cucumberjs 2.0, should be –cucumberOpts.tags ‘not @ignore and (@smoke or @rt)'

    So I tried:
    protractor ./FM_IntTest_UI_conf.js –cucumberOpts.tags ‘not @ignore and (@smoke or @rt)’

    AND to be more consisten with what you document above, I trie also this:

    protractor ./FM_IntTest_UI_conf.js –tags ‘not @ignore and (@smoke or @rt)’

    Neither Worked. For both I got the following error:

    ‘ was unexpected at this time.
    
    C:\workspace>  “C:\workspace\node_modules\.bin\\node.exe”  “C:\workspace\node_modules\.bin\\..\protractor\bin\protractor” ./FM_IntTest_UI_conf.js —tags ‘not @ignor
    e and (@smoke or @rt)’
    
    npm ERR! Test failed.

    See above for more details.

    What is now the correct syntax?

    • Wim Selles

      Hi Kyon,

      Strange thing is that when I use your tags in my own project it works –-cucumberOpts.tags 'not @ignore and (@smoke or @rt)' (also got a smoke and a @ignore in my project, mind the double --).

      I’m working on a Mac and you are on Windows? If so, have you also tried double quotes in stead of single?

      The --tags is only for using a standalone version of CucumberJS, for Protractor use --cucumberOpts.tags. Can you also post which versions you are using of:

      – Protractor
      – CucumberJS
      – protractor-cucumber-framework

      Tnx in advance

Leave a Reply