AngularJS End to End Testing with Protractor - Easy Set up with Yeoman, Grunt, Bower

Most likely you already know that AngularJS version 1.2 is deprecating the built-in “end-to-end” or E2E scenario runner with Protractor. This post outlines the steps for setting up Protractor using Yeoman, Grunt and Bower to test an application against multiple targeted browsers in parallel.

Before we dive in, here is a quick background if you are new to Protractor or E2E testing. Protractor is an “end-to-end” functional testing framework for AngularJS. Protractor could help you test the application by running it in the real browser and performing interactions like clicking a link, button, filling out forms and so on. Protractor is built on top of WebDriverJS which is JavaScript bindings for Selenium. Protractor wraps WebDriverJS and adds AngularJS specific functionalities.

Awesome Yo

We will be using the awesome Yeoman for scaffolding our sample application. If you don’t already have Yeoman, install it by running

npm install -g yo

We will need a generator for AngularJS, install it by running

npm install -g generator-angular

Now let’s generate an application by running following in the application directory

yo angular App1

Add Protractor to Application

We will be using the Yeoman generator generator-protractor for adding Protractor to our application. Install the generator and then run it

npm install -g generator-protractor
yo protractor

It would install Protractor, Selenium and prompt for the config file name, application URL, specs directory, etc. It would add a sample spec under the spec directory, replace the example tests with a simple one shown below, it only checks for the presence of the application name on the home page. Protractor uses Jasmine framework by default, if you prefer Mocha instead you could read more about it here. I supplied ‘App1’ while running the yo angular generator, substitute it with the name of your application.

describe('Application Homepage', function() {
  it('should display the application name', function() {
    browser.get('http://localhost:9000');

    var appName = element(by.css('h3.text-muted')); //using the CSS selector

    expect(appName.getText()).toEqual('App1');
  });

}); 

Now we are ready to execute our first Protractor test. We need to make sure our web server and Selenium server are running. Start the web server by running grunt server if you haven’t already. Start the Selenium server in new terminal by running

./node_modules/protractor/bin/webdriver-manager start

Now we can execute our tests by running the Protractor binary passing path to the config file we generated earlier

./node_modules/protractor/bin/protractor protractor-config.js

You will see an instance of Chrome launching and navigating to your application to perform the tests, you will see the results in the terminal.

Testing With Multiple Browser

By default, Protractor is configured to run tests in Chrome as you can see it in the capabilities section of the protractor config file.

  capabilities: {
    'browserName': 'chrome'
  } 

You can replace the browserName ‘chrome’ with ’safari’ or ‘firefox’ and Protractor would use a respective browser to test your application. You could create a separate protractor config file and launch protractor runner with the specific file as an argument. Manually running for each target browser wouldn’t fit well in the development workflow, so let’s automate it using Grunt.

Let’s Automate It

First create a Grunt task for running our tests, we will be using a Grunt plugin grunt-protractor-runner for this, install it by

npm install grunt-protractor-runner --save-dev //’save-dev’ to register as development dependency

Once the plugin is installed, lets load it by adding following line in our Gruntfile

grunt.loadNpmTasks('grunt-protractor-runner');

Now we could go ahead and define a task called ‘protractor’ in Gruntfile’s Grunt.initConfig call as shown below.

    protractor: {
        options: {
              configFile: "protractor-config.js”, //your protractor config file
              keepAlive: true, // If false, the grunt process stops when the test fails.
              noColor: false, // If true, protractor will not use colors in its output.
              args: {
                  // Arguments passed to the command
              }
          },
        chrome: {
            options: {
                  args: {
                      browser: "chrome"
                  }
              }
        },
        safari: {
            options: {
                args: {
                    browser: "safari"
                }
            }
        },
        firefox: {
            options: {
                args: {
                    browser: "firefox"
                }
            }
        }
    } 

You could see I have defined a section for Chrome, Safari and Firefox followed by the main options block. Now you could execute your tests against 3 browsers by running

grunt protractor

You will see it launching an instance of each of this browser one by one and running your tests. We just have one simple test, but you might already see that it’s slow. In next section, we will configure it to run tests for each targeted brewer in parallel.

Run it in Parallel

We will use a Grunt plug-in called grunt-parallel to run tasks in parallel. This plugin is already included as a part of Angular generator, if you don’t see it under the node_modules of your project, go ahead and install it by npm install grunt-concurrent --save-dev. As a first step, let’s break test against each targeted browser as a separate task by registering it in the Gruntfile

  grunt.registerTask('protractor-chrome', ['protractor:chrome']);
  grunt.registerTask('protractor-safari', ['protractor:safari']);
  grunt.registerTask('protractor-firefox', ['protractor:firefox']); 

Now, let’s register these tasks to run in parallel. In Gruntfile’s Grunt.initConfig call, under the concurrent section add a task protractor_test as shown below

concurrent: { 
     protractor_test: ['protractor-chrome', 'protractor-safari', 'protractor-firefox’]  
}

In the Gruntfile, define a task called protractor-e2e to run this concurrent

grunt.registerTask('protractor-e2e', ['concurrent:protractor_test']); 

You could run this task by grunt protractor-e2e and see now your tests are running much faster as those are executed in parallel. Last friction to remove from our development workflow is to run tests automatically when files are updated.

Run Tests on File Change

Let’s execute the test whenever the file changes by using the Grunt plugin grunt-contrib-watch. It has already been added and configured, so it is just matter of adding following configuration in watch section, under Grunt.initConfig.

e2eTest: {
        files: ['spec/{,*/}*.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '<%= yeoman.app %>/{,*/}*.html',
                '.tmp/styles/{,*/}*.css',
                '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'],
        tasks: ['protractor-e2e']
      } 

You are all set, happy testing! The sample application with all the configuration and code could be found on Github.