Skip to content

Latest commit

 

History

History
201 lines (151 loc) · 5.75 KB

README.cucumber.md

File metadata and controls

201 lines (151 loc) · 5.75 KB

Cucumber Usage

Feature Files

The first step is to create a directory named "features". Cucumber will look for *.feature files contained in this directory.

After putting a couple of files in there (based on the end-to-end tests written for Protractor), Cucumber will produce output like this:

$ ./node_modules/cucumber/bin/cucumber.js --format pretty
Feature: View1

  This is the place where you justify the View1 feature and put it in context of the
  whole product.


  Scenario: User navigates to View1    # features/view1.feature:6
    Given the browser is open          # features/view1.feature:7
    And the node server is running     # features/view1.feature:8
    When the user navigates to /view1  # features/view1.feature:9
    Then we see text indicating view 1 # features/view1.feature:10


Feature: View2

  This is the place where you justify the View2 feature and put it in context of the
  whole product.


  Scenario: User navigates to View2    # features/view2.feature:6
    Given the browser is open          # features/view2.feature:7
    And the node server is running     # features/view2.feature:8
    When the user navigates to /view2  # features/view2.feature:9
    Then we see text indicating view 2 # features/view2.feature:10
2 scenarios (2 undefined)
8 steps (8 undefined)

You can implement step definitions for undefined steps with these snippets:
this.Given(/^the browser is open$/, function (callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

this.Given(/^the node server is running$/, function (callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

this.When(/^the user navigates to \/view(\d+)$/, function (arg1, callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

this.Then(/^we see text indicating view (\d+)$/, function (arg1, callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

One thing to notice is that Cucumber detected some opportunities for making parameterized steps. For example, the following two steps in the original feature files ...

  • When the user navigates to /view1
  • When the user navigates to /view2

... were combined into a single parameterized step stub:

this.When(/^the user navigates to \/view(\d+)$/, function (arg1, callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

Similarly, Cucumber combined the "Then we see text..." steps.

Step Definition Stubs

Step definitions are maintained in the subdirectory features/step_definitions. You can put them in any Javascript file (*.js). The stubs shown above can be copy-pasted into a step definition file stub that looks like this:

'use strict';

var wrapper = function() {
    // TODO: Paste step stubs here
};

module.exports = wrapper;

Once the stubs are pasted into this stub, Cucumber will report "pending" steps (which are not yet implemented), and subsequent "skipped" steps that could not be executed because they follow a "pending" step.

$ ./node_modules/cucumber/bin/cucumber.js
P---P---

2 scenarios (2 pending)
8 steps (2 pending, 6 skipped)

Protractor World

In order to use Cucumber with Protractor, we use the protractor-cucumber package.

npm install protractor-cucumber --save-dev

This module (features/step_definitions/browser-world.js) creates a World class that connects to an external Selenium server.

// features/step_definitions/browser-world.js
// Creates a Cucumber World object that is connected to Selenium.

var pc = require('protractor-cucumber');

var steps = function() {
  var seleniumAddress = 'http://localhost:4444/wd/hub';
  var options = { browser : 'chrome', timeout : 100000 };
  this.World = pc.world(seleniumAddress, options);
};

module.exports = steps;

With this in place, we can now start to implement the steps in view-steps.js. In particular, we can refer to this.browser to refer to the Selenium WebDriver object. A new browser is instantiated for each scenario.

Launching the Backend

In order to accomplish integration testing, we want to run our backend along with our client (in the browser). We can do this with an Around construct, which allows us to run complementary code before and after each scenario. In this case, our backend is minimal, consisting of a simple HTTP server.

  this.Around(function (runScenario) {
    // Create a node-static server instance to serve the project root directory.
    var staticServer = new nodeStatic.Server('./');
    // Create an HTTP server.
    http.createServer(function (request, response) {
      request.addListener('end', function() {
        staticServer.serve(request, response);
      }).resume();
    }).listen(8000, function (err) {
      assert(!err);
      var httpServer = this;

      // Now run the scenario, with our clean-up "after" code as a callback.
      runScenario(function (callback) {
        // Shut down the browser.
        var world = this;
        world.browser.quit()
          .then(function () {
            // Shut down the HTTP server.
            httpServer.close(callback);
          });
      });
    });
  });

Running tests

Because we use Selenium, we must run the Selenium server as a separate process. We can just keep this running.

$ ~/angular-seed/node_modules/.bin/webdriver-manager start &

Next, we can run Cucumber. We have this hooked up to an npm script:

$ npm run cucumber

> angular-seed@0.0.0 cucumber /home/mattfrantz/angular-seed
> cucumber.js

............

2 scenarios (2 passed)
8 steps (8 passed)