Getting Started with WebDriverJS & Mocha

See also: WebDriverJS & Mocha Part 2: Hooks

As more and more companies move towards having web user interfaces built using JavaScript (eg. React & AngularJS), as well as towards full stack JavaScript (Node.js), it seems JavaScript is becoming a ubiqutous programming language, with more and more developers mastering it.

It makes sense then to start writing automated acceptance tests in JavaScript using Node.js, as this lowers the barrier for more people to write acceptance tests.

I’ve recently started using WebDriverJS (the official Selenium JavaScript library) and Mocha to write automated acceptance tests for WordPress.com, and I want to share some of my findings so far.

It takes a bit of getting used to, as the library is asynchronous it’s quite different to watir-webdriver, or Selenium in C# or Java.

There’s not a lot of information about it on the Internet either so a lot of the time you’re on your own to experiment and get things working.

As far as installation goes, all you need is Node.js and chromedriver, which you can install via homebrew on Mac OSX.

You’ll just need two npm packages:

npm install selenium-webdriver@2.46.1
npm install -g mocha

I started with a JavaScript file something like this, expecting it to work:

var assert = require('assert');
var webdriver = require('selenium-webdriver');

describe('Ralph Says', function() {
  it('shows a quote container', function () {
    var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
    driver.get('http://ralphsays.github.io');
    var present = driver.isElementPresent(webdriver.By.id('quote'));
    assert.equal(present, true, "Quote container not displayed");
    driver.quit();
  });
});

But when running it using:

mocha spec.js

it throws an assertion error straight away!

AssertionError: Quote container not displayed

It doesn’t even open the browser!

This is because WebDriverJS is asynchronous meaning it won’t automatically wait for one command to run before running another, so it doesn’t wait for a browser before doing stuff to that browser. Getting your head around that is really hard when coming from a nice synchronous API like watir-webdriver.

Thankfully, WebDriverJS uses ‘promises’ to be able to write code that runs in a specified order, and a ‘promise manager’ to make sure this happens.

There’s a few things you need to do to get this to work.

Firstly: you can’t use the default Mocha methods, you need to use the WebDriverJS Testing Module if you’re using Mocha. This means the above example becomes:

var assert = require('assert');
var webdriver = require('selenium-webdriver');
var test = require('selenium-webdriver/testing');

test.describe('Ralph Says', function() {
  test.it('shows a quote container', function () {
    var driver = new     webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
    driver.get('http://ralphsays.github.io');
    var present = driver.isElementPresent(webdriver.By.id('quote'));
    assert.equal(present, true, "Quote container not displayed");
    driver.quit();
  });
});

It now at least opens a browser, but fails with a different error:

Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

Mocha has a quite low maximum time it will allow a test to execute for before automatically failing. Since we’re writing acceptance tests we will want it to be more than 2 seconds, we can set it to 30 seconds.

var assert = require('assert');
var webdriver = require('selenium-webdriver');
var test = require('selenium-webdriver/testing');

const mochaTimeOut = 30000; //ms

test.describe('Ralph Says', function() {
  this.timeout(mochaTimeOut);  
  test.it('shows a quote container', function () {
    var driver = new     webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
    driver.get('http://ralphsays.github.io');
    var present = driver.isElementPresent(webdriver.By.id('quote'));
    assert.equal(present, true, "Quote container not displayed");
    driver.quit();
  });
});

It’s now opening the browser, but we’re back to our original error.

AssertionError: Quote container not displayed

The promise library helps us wtih ordering, but anything that retrieves a value from the browser (like getting a text value, whether an element exists, getting the browser title) must be written using a promise. This looks like this:

var assert = require('assert');
var webdriver = require('selenium-webdriver');
var test = require('selenium-webdriver/testing');

const mochaTimeOut = 30000; //ms

test.describe('Ralph Says', function() {
  this.timeout(mochaTimeOut);
  test.it('shows a quote container', function () {
    var driver = new     webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
    driver.get('http://ralphsays.github.io');
    driver.isElementPresent(webdriver.By.id('quote')).then(function(present) {
      assert.equal(present, true, "Quote container not displayed");
    });
    driver.quit();
  });
});

Our output looks like this:

 Ralph Says
    ✓ shows a quote container (2694ms)


  1 passing (3s)

Great success! We’ve sucessfully written our first automated acceptance test in JavaScript using WebDriverJS and Mocha (using the testing module).

In my next blog post I will explore how we can create page objects so we don’t duplicate application specific code throughout our test specifications.

Author: Alister Scott

Alister is an Excellence Wrangler for Automattic.

15 thoughts on “Getting Started with WebDriverJS & Mocha”

  1. Alister,
    Btw cool article :)

    I wonder, why just node.js+selenium+mocha
    Why not protractor+jasmine or protractor+cucumber or protractor+mocha?
    just wondering :)

    Like

    1. Hi Yuri
      We’re using React not Angular, and Protractor is fairly tied into Angular with all its synchronising stuff.
      You can disable it, but defeats the purpose when Protractor is just using WebDriverJS underneath anyway :)

      Like

      1. Hi Alister,
        Ah, I see, I didn’t understand that at first. Thanks for clarification.
        I’m still with Watir-Webdriver and Ruby :)
        But new trends are very interesting. Will try to figure out and try on our projects…

        Like

        1. I’d rather still be using watir-webdriver; it’s much simpler. But on a full stack JavaScript project it makes more sense to use JS so that all the devs can work on tests too :)

          Like

          1. Yes, here we have the eternal question :)
            How divide effectively who should write auto tests…
            Is it suitable that testers make scenarios and developers script them
            Or testers should make all this stuff :)

            Like

  2. Here’s hoping that the transition from the page-object gem to JS (selenium-pageobject?) is equally empowering. I’m excited to see where you go with this Alister. Thanks for the great and timely article. I might just stop considering WatiN as the forward-thinking best way to go.

    Like

  3. When i try the first snippets i get an exception from selenium-webdriver..

    is it just me?

    node_modules\selenium-webdriver\_base.js:104
    vm.runInContext(opt_srcText, closure, src);
    ^
    SyntaxError: Unexpected token )
    at goog.loadModuleFromSource_ (C:\Users\michael.kernaghan\node_modules\selen
    ium-webdriver\lib\goog\base.js:1123:19)

    Like

    1. Yes, I went through and evaluated about 10 different libraries.
      I really dislike the function chaining in WebDriverIO as it makes it hard to read and hard to debug tests. Plus it makes it almost impossible to use page objects, unless they look terrible like the ones suggested in here.
      The promises in WebDriverJs take getting used to but they aren’t that bad, and it’s easy to create nice page objects.
      The other contender was WebDriverSync but it’s dependencies are too heavyweight (it uses the Java bindings underneath) and I couldn’t get it running on CircleCI at all.

      Like

Comments are closed.