First, before getting right into Cucumber for Angular, you should know about the test framework that sits between them.  Protractor is a Node.js module that facilitates testing of Angular apps because it has the ability to access Angular variables attached to the scopes of all the objects in the DOM.  It provides the Selenium WebDriver library for you, thus offering wrapper functions for the WebDriver calls you are accustomed to writing in order to make your Web application's UI do your bidding.

The idea of this page is not to go deeply into writing test logic with Protractor, since other sites delve into the topic.  Instead, it covers the very basics of getting started with Cucumber with Protractor, and what it takes to run a basic test.

What environment setup does this require?


  1. Install Node.js.
  2. Using Node Package Manager (npm), install the packages "protractor", "cucumber", and "chai".  On a Mac, you must run npm as root.

How do I wire my Gherkin syntax into JavaScript functions?


First, let's step back and explain a little bit about Protractor.  When you install it, a binary called "protractor" is placed in the "node_modules" directory in wherever you're located (unless you ran "npm install -g" which puts it in a user folder such as /usr/local/lib/node_modules).  You use this binary with a configuration file passed in as an argument to kick off your test in Protractor.  A configuration file is a collection of declarations and objects in JavaScript.  A complete (but basic) Protractor config file looks like this:

conf.js:


var protractor = require('protractor');  // Include the Protractor JavaScript library
var path = require('path');              // Handle paths
var fs = require('fs');                  // Handle the filesystem

exports.config = {
  framework: 'cucumber',                  // Specify that Protractor should use Cucumber rather than the default Jasmine
  cucumberOpts: {
    // define your step definitions in this file
    require: 'steps.js',
    format: 'progress'
  },
  baseUrl:  'http://website-under-test',
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['cucumberTest.feature'],        // An array of tests to run (wildcards and !'s are supported)
  multiCapabilities: [                    // Array of "capabilities" objects to define test environment settings
    {
      'browserName''chrome',
    }, {
      'browserName''internet explorer'
    }
  ],
  onPrepare: function() {}                // Optional function to load extra variables into your test
};

The file above will load a browser, point it to http://website-under-test, and use the Selenium WebDriver instance at localhost:4444 to drive the site under test in the manner described by the spec file, defined here as "cucumberTest.feature".  Before you can run protractor conf.js, you must also run webdriver-manager start from the same directory in order to start your Selenium instance.  You should also write your Gherkin syntax and underlying JavaScript routines as well.

Ok, how do I really wire my Gherkin syntax into JavaScript functions?


I hope you are already familiar with Gherkin syntax; after all, you are the one who defines it.  The JavaScript sets the boundaries for what names of Gherkin steps will be accepted by the test runner, but the desired names for these steps that get coded into the JavaScript might be defined by a business process rather than the software or test developers, depending on who the end users are.  Here is a short example file which defines several Gherkin steps, and some underlying logic:

steps.js:


var expect = require('chai').expect;

module.exports = function() {
  /************************************
  * These are your keywords in Gherkin
  ************************************/

  // Given I go to "____"
  this.Given(/^I go to "([^"]*)"$/function(url, next) {
    browser.get('http://website-under-test' + url);
    next();
  });

  // Then I click on "____"
  this.When(/^I click on "([^"]*)"$/function(url, next) {
    clickOn(selector, next);
  });

  // Then I should see "____"
  this.Then(/^I should see "([^"]*)"$/function(content, next) {
    assertPageContains(content, next);
  });

  // Then I should see "____" on "____"
  this.Then(/^I should see "([^"]*)" on "([^"]*)"$/function(content, selector, next) {
    assertElementContains(selector, content, next);
  });

  /********************************************************
  * This is the underlying code backing the Gherkin syntax
  * When this section gets big, split it into another file
  ********************************************************/

  function assertPageContains(content, next) {
    browser.getPageSource().then(function(source) {
      expect(source).to.contain(content);
      next();
    });
  }

  function assertElementContains(selector, content, next) {
    element(by.binding(selector)).getText().then(function(text) {
      expect(text).to.equal(content);
      next();
    });
  }

  function clickOn(selector, next) {
    element(by.xpath(selector)).click();
     next();
  }
};

First, you notice the requirement for Chai's "expect" module.  Chai is an assertion engine that provides the means to actually do analysis on the items under test (i.e. it provides expect(), to(), equal(), contain(), and so forth).  It is written in order to allow you to make English-looking statements with JavaScript, as you saw above with expect(source).to.contain(content).  Normally, Protractor would include the Jasmine framework which already incorporates assertion functions, but since you need to request the Cucumber framework to run Cucumber tests, you must also load your own assertion engine.

Several things in here are provided by Protractor, including the browser object, the element() function which looks for an element based on the means you specify (of most interest, notice where we chose by.binding() to find an element bound to an Angular variable specified by the user in the Gherkin text -- it's always fun when the UI team doesn't define IDs for all elements ), various means to interact with the element such as click(), and also then(), which relies on JavaScript Promises rather than callbacks in order to provide a bit more robust asynchronous method execution.

With these tasks in place, and the underlying logic to support them, you can then write Gherkin such as:

cucumberTest.feature:


Feature: Track time took for task
  As a user of Pomodoro.cc
  I should be able to see the dashboard
  So that I can track time

  Scenario: Visiting /
    Given I go to "/"
    Then I should see "Start Timer"
    Then I should see "00:00"



And you will be so happy to see the application successfully run these tasks.  Right?  

Reference: Thank goodness for this super-basic intro to Gherkin at http://christian.fei.ninja/Write-your-Protractor-tests-in-Cucumber/, without which I could not have started on Gherkin myself.  The reason for my post is to fill in some of the additional details regarding setting all this up in your environment, plus to expound upon some of the things JavaScript supports.