Recently, I’ve had a few people ask me how to use webdriverio with cucumber. It sounded like something that should have been easy, but after a brief search on the internet, all I could find were a few blog post tutorials (that all contradicted each other) and some complicated boilerplate code on the webdriverio website. There was nothing for the beginner who wants to create their first BDD framework using JavaScript.
So, after a bit of experimentation, I’ve managed to make everything work together. I imagine both cucumber and webdriverio have changed since the other blog posts have been written, which would explain why they’re all different why none of them work. With that in mind, if you try this and it fails, please do let me know and I’ll take a look and update it. I’ll also try to explain things as I go along, but if you’re already proficient in JavaScript, just take a quick look at the code snippets. That’s all you’ll need.
Lots of this will be performed on your terminal, so go ahead and launch it. This post assumes you already have Node installed and a good text editor. If you don’t have either of these, check out the ‘Prerequisites’ section of my previous webdriverio tutorial.
Now, in your terminal, navigate to wherever you store your projects. Mine all live in a directory called ‘code’ that sits in my home directory.
First we need to create a new project folder and navigate there. You can do this in one line:
mkdir webdriverio_cucumber_poc && cd webdriverio_cucumber_poc
Next we need to initialise it with NPM (Node Package Manager) to make it a proper node project:
npm init -f
This just creates a package.json
file with the default options. Feel free not to use the -f
argument if you want to be asked some questions for a more comprehensive file.
Now, we need to install webdriverio:
npm install webdriverio --save-dev
This will install webdriverio and the --save-dev
argument will save it in yourpackage.json
file for the next person. This means that when you have a project that requires lots of npm packages, new team members don’t need to install them individually. They just need yourpackage.json
file and then they can install them all in one go with npm install
.
Now that webdriverio is installed, we can run it with the config
parameter to help us set up our project:
./node_modules/.bin/wdio config
You will now be asked a bunch of questions. I’ve detailed below the answers you need for this tutorial, but feel free to experiment, of course:
Q: Where do you want to execute your tests?
A: On my local machineQ: Which framework do you want to use?
A: cucumberQ: Shall I install the framework adapter for you?
A: Yes (just press enter)Q: Where are your feature files located?
A: ./features/**/*.feature (just press enter)Q:Where are your step definitions located?
A: ./features/step-definitions (just press enter)Q: Which reporter do you want to use?
A: specQ: Shall I install the reporter library for you?
A: Yes (just press enter)Q: Do you want to add a service to your test setup?
A: Selenium Standalone ServiceQ: Shall I install the services for you?
A: Yes (just press enter)Q: Level of logging verbosity:
A: silent (just press enter)Q: In which directory should screenshots gets saved if a command fails?
A: ./errorShots/ (just press enter)Q: What is the base url?
A: http://localhost (just press enter)
Now, we need to create a couple more folders to store our features & step definitions:
mkdir features && cd features
mkdir step-definitions
We now have the basis of our test project. All we need to do now is write a small feature file then some associated step definitions. The line of code below will create the files we need.
touch test.feature && touch step-definitions/test-steps.js
Now, launch your favourite text editor and navigate to this project directory. Now, open the test.feature
file. Next, copy & paste the following into it and save:
Feature: Cucumber proof of concept
Scenario: First Scenario
Given I navigate to Google
When I search for "Formula 1"
This will be our test feature. It doesn’t do much. It simply launches a browser, navigates to Google, then searches for a string of text. It doesn’t have any assertions and doesn’t do any real testing. However, this tutorial is purely for setting up webdriverio and cucumber and this is sufficient for that. Writing good webdriverio tests will be for another post.
Now, because of the unique way that webdriverio interacts with cucumber, we need to make a change to our config file. If you cast your mind back to when you were setting up this project earlier, you were asked a bunch of questions and provided answers. You were asked where your features would live and also where your step definitions would be found. If you have a good memory (or failing that, you can scroll back up and look at the answers), you will remember that the location of the features was a path that contained globbing. The path contained wildcards (*). This is because webdriverio supports globbing and these wildcards can mean you can be more generic with your pathsWith globbing, you can create more directories if you like, within the main one, and webdriverio will find them just fine.
However, even though we’re running our tests through webdriverio, once we’ve found our feature files, we’re using cucumber to find and run our step definitions. This is where it can be a bit confusing as cucumber doesn’t support globbing. In layperson’s terms, we can specify a folder where we can store our feature files and webdriver will find them, but we need to specify the exact files when we create our step definitions for cucumber.
Open wdio.conf.js
in your text editor. There are a lot of comments (greyed out text) in there, but you should be able to scroll down and find your cucumber options section. It should look a lot like this (I’ve just removed the comments to save space):
cucumberOpts: {
require: ['./features/step-definitions'],
backtrace: false,
compiler: [],
dryRun: false,
failFast: false,
format: ['pretty'],
colors: true,
snippets: true,
source: true,
profile: [],
strict: false,
tags: [],
timeout: 20000,
ignoreUndefinedDefinitions: false,
},
It’s the first line that we’re interested in. That first line gives the location of our step definition files, but that’s not enough for cucumber. We need to tell it the exact file. Change it to the following:
require: ['./features/step-definitions/test-steps.js'],
Now that our feature file is created and cucumber knows where our step definitions are located, we can run our tests and see what happens:
./node_modules/.bin/wdio wdio.conf.js
You should see Firefox launch, but not really do anything. You should then see the results in your terminal. Which will have failed, but that’s ok! We haven’t written any step definitions yet. It’s supposed to fail at this point.
Let’s write our step definitions to make the test pass. Open step-definitions/test-steps.js
in your editor, then copy & paste the following code:
let {defineSupportCode} = require('cucumber');
defineSupportCode(function({Given, When, Then}) {
});
What we’re doing here is telling our framework that when we use our defineSupportCode
variable, we’re using the function defined in cucumber. We’re then creating a place for our step definitions that cucumber will be able to find. Next, we need to create our first step definition. Copy & paste the following code:
Given('I navigate to Google', () => {
browser.url('http://www.google.com')
});
So that your test-steps.js
file looks like this:
let {defineSupportCode} = require('cucumber');
defineSupportCode(function({Given, When, Then}) {
Given('I navigate to Google', () => {
browser.url('http://www.google.com')
});
});
Now run the tests and you should see that the first step passes, Google is loaded in Firefox, but then the next step fails. Your output should tell you, in red, that your step is not defined. Let’s define it now. Copy & paste the following in your steps file, after the first step:
When('I search for "{string}"', (text) => {
browser.setValue('#lst-ib', text);
browser.pause(5000);
});
Your completed test-steps.js
file should now look like this:
let {defineSupportCode} = require('cucumber');
defineSupportCode(function({Given, When, Then}) {
Given('I navigate to Google', () => {
browser.url('http://www.google.com')
});
When('I search for "{string}"', (text) => {
browser.setValue('#lst-ib', text);
browser.pause(5000);
});
});
When you add more step definitions, simply do what you’ve just done and add them in after your last definition. I’ll leave explanations of how step definitions in JavaScript are structured for another tutorial, but you should be able to understand what’s going on just by looking at them.
Now, run your tests one more time:
./node_modules/.bin/wdio wdio.conf.js
They pass!
You should have seen Firefox start up, navigate to Google, then type in ‘Formula 1’. If you look into our step definitions, you’ll notice that text in the first step is matched exactly in the first step definition, but in the second step, we’re accepting any string. This means that you can replace ‘Formula 1’ with any text you like and the test will still work.
I hope that has helped. You can now create your very own BDD framework using webdriverio and cucumber. Enjoy!