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
- Protractor
- CucumberJS
- TypeScript
- and since 22nd of May also a default reporting module (multiple-cucumber-html-reporter, see also this post)
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 usescenarioResult.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
andBefore
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:
- Image from Eldon Zigarlick on Twitter
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:
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