The go to solution for creating automated end to end tests when building AngularJS applications is Protractor. In this post, I am not going to explain how to write tests using Protractor. Instead I’m going to cover some of the things I discovered when trying to integrate a suite of Protractor tests into a TeamCity based CI/CD pipeline.

Protractor Basics

The following article cover the basics of Protractor. This formed the basic understanding that I needed to work out where all the moving parts were and therefore work out how best to integrate into a CI/CD pipeline.

http://www.protractortest.org/#/

Protractor is a node module so I’ll assume that you have nodejs installed on your development machine. Once you have that you need to get protractor using

npm install protractor -g

Protractor relies on drivers for the various web browsers. It will execute your E2E test scenarios directly in the browser so this bit is important. The drivers in turn use Selenium, a common testing framework.

That is a lot of moving parts I hear you say. Luckily it is relatively simple to get this up and running. Once you have Protractor ensure you have all the latest webdriver bits

webdriver-manager update

This ensures that all the latest webdriver and selenium versions are downloaded and configured correctly. The final step is to start everything up. You can do that with

webdriver-manager start

You may hit your first hurdle at this point. Selenium is a Java based solution and as such requires Java to be installed on your development machine. So if you have a problem at this stage go off to Oracle’s site, download Java and install it. You’ll need the latest JDK.

In order to continue you’ll need an example test spec, spec.js. I used this one which is part of the Protractor tutorial.

describe('Protractor Demo App', function() {
    var firstNumber = element(by.model('first'));
    var secondNumber = element(by.model('second'));
    var goButton = element(by.id('gobutton'));
    var latestResult = element(by.binding('latest'));
    var history = element.all(by.repeater('result in memory'));

    function add(a, b) {
         firstNumber.sendKeys(a);
         secondNumber.sendKeys(b);
         goButton.click();
     }

     beforeEach(function() {
         browser.get('http://juliemr.github.io/protractor-demo/');
     });

     it('should have a history', function() {
         add(1, 2);
         add(3, 4);

         expect(history.count()).toEqual(2);

         add(5, 6);

         expect(history.count()).toEqual(3); // This is wrong!
     });
});

You also need a configuration file, conf.js. This is the one used in the tutorial.

exports.config = {
    framework: 'jasmine',
    seleniumAddress: 'http://localhost:4444/wd/hub',
    specs: ['spec.js']
}

To run this selenium must be running locally exposing an endpoint on port 4444. But if you remove the line starting seleniumAddress… guess what… an instance is started for you automatically. With the modified conf.js file you should be able to run your test with the following command.

protractor conf.js

This should work if

  1. You have installed protractor globally
  2. You have ensured that the web drivers and selenium are updated.
  3. And all the versions of protractor, your browser and its particular web driver are all aligned

When I was trying to make an old implementation of a Protractor test suite CI/CD ready. It turned out that it was using an old version of protractor so updating the web drivers downloaded old versions. The machine I was testing this on happened to have an old version of chrome so all seemed to work. This subsequently failed when other people tried because they were using the latest versions of chrome. An upgrade to protractor fixed that but then caused problems for people stuck on older versions.

You might think why are people on old versions of chrome? Blame centralised management of software in big organisations”

So the lesson I learned here was to always start with the latest versions, even when picking up older implementations. The version of the framework and the browser matter.

Making this CI/CD Friendlier

With the CI/CD pipeline I was working with I didn’t want to have an instance of the webdrivers and Selenium running constantly. Neither did I want to have to manually start up the web driver on each build. As Gulp was being used for other purposes I happened on gulp-angular-protractor which provides a wrapper for all of these things. Effectively it updates the webdrivers and starts them for you and then clears them down when you have finished. This meant that I could have a single gulp task that executed my test suite.

A good build is self-contained and minimises dependencies on what is installed on the build server. The packages I need are installed locally. In order to create a package.json file install the required packaged like this

npm install protractor -save-dev
npm install gulp-angular-protractor -save-dev
npm install gulp -save-dev

Although the final step installs gulp locally it also needs to be installed globally too. I have no idea why but it does. You can then create a Gulp file like this one

let gulp = require("gulp");
let gulpProtractorAngular = require("gulp-angular-protractor");

gulp.task("runtests", callback => {
    gulp
        .src(["myspec.js"])
        .pipe(gulpProtractorAngular({
            configFile: "protractor.conf.js",
            debug: false,
            autoStartStopServer: true
        }))
        .on("error", e => {
            console.log(e);
        })
        .on("end", callback);
});

Lets break this down.

First, I’m bringing in gulp and gulp-angular-protractor so I can use them later. Next, I’m creating a gulp task called “runtest”. The rest basically says execute conf.js with spec.js as the test spec file.

As this point you have a basic spec and conf file that can be executed from a Gulp task. This is enough to start building some real test specs and checking them out. Come back next time to see what you need to do to integrate this properly with TeamCity.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s