e2e testing with protractor

Seriously? e2e?

Ronen Amiel

WHO ARE YOU?

  • Software developer @wix and @codeoasis
  • Developing the wix app market platform
  • Top notch battlefield gamer

why is testing so important?

Testing is about gaining confidence that your code does what you think it should do

AREN'T UNIT TESTS ENOUGH?

what e2e tests are for?

  • Do all the pieces work together?
  • Can I release my code to production?
  • Is my view working the way it should?

what are the problems with e2e testing?

  • Installing and configuring selenium is painful
  • Tests are difficult to write
  • Tests are hard to debug
  • Keeping tests up to date is requires a lot of work

meet protractor

setup

  • Install node.js
  • npm install -g protractor
  • webdriver-manager update

create a config file

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

write your tests

describe('angularjs homepage', function() { 
  it('should have a title', function() { 
    browser.get('http://juliemr.github.io/protractor-demo/'); 
    expect(browser.getTitle()).toEqual('Super Calculator');
  });
});

let's run it

protractor api

  • Browser: get(), refresh(), close()
  • Element: by.model(), by.binding, by.className()
  • Protractor: waitForAngular()

can i have an example?

 describe('slow calculator', function() {
  beforeEach(function() {
    browser.get('http://localhost:3456');
  });

  it('should add numbers', function() {
    element(by.model('first')).sendKeys(4);
    element(by.model('second')).sendKeys(5);

    element(by.id('gobutton')).click();

    expect(element(by.binding('latest')).getText()).
        toEqual('9');
  });

  describe('memory', function() {
    var first, second, goButton;
    beforeEach(function() {
      first = element(by.model('first'));
      second = element(by.model('second'));
      goButton = element(by.id('gobutton'));
    });

    it('should start out with an empty memory', function () {
      var memory =
          element.all(by.repeater('result in memory'));

      expect(memory.count()).toEqual(0);
    });

    it('should fill the memory with past results', function() {
      first.sendKeys(1);
      second.sendKeys(1);
      goButton.click();

      first.sendKeys(10);
      second.sendKeys(20);
      goButton.click();

      var memory = element.all(by.repeater('result in memory').
          column('result.value'));
      memory.then(function (arr) {
        expect(arr.length).toEqual(2);
        expect(arr[0].getText()).toEqual('30'); // 10 + 20 = 30
        expect(arr[1].getText()).toEqual('2'); // 1 + 1 = 2
      });
    });
  });
});

let's quickly go over protractor's features

selecting by binding

In your test
 element(by.binding('name'));
In your app
 <div>{{ name }}</div>

selecting by model

In your test
 element(by.model('age'));
In your app
 <input type="text" ng-model="age" />

selecting by repeater

In your test
 element(by.repeater('cat in pets').row(1));
In your app
<div ng-repeat="cat in pets">
  <span>{{cat.name}}</span>
  <span>{{cat.age}}</span>
</div>

By css containing text

In your test
 element(by.cssContainingText('.pet', 'Dog'));
In your app
 <ul>
  <li class="pet">Dog</li>
  <li class="pet">Cat</li>
</ul>

You can also trigger events!

trigger a click

In your test
 element(by.css('[ng-click="submit()"]')).click();
In your app
 <button ng-click="submit()"><button>

press keys

In your test
 element(by.model('commentText')).sendKeys("Hi!", protractor.Key.ENTER);
In your app
 <textarea ng-model="commentText"><textarea>

interested in more?

The element explorer

Playground for debugging tests

  • webdriver-manager start
  • ./bin/elementexplorer.js http://angularjs.org

page objects

Separate the code where you find the elements on the web page, from the test logic itself

An example of a page object

var AngularHomepage = function() {
  this.nameInput = element(by.model('yourName'));
  this.greeting = element(by.binding('yourName'));

  this.get = function() {
    browser.get('http://www.angularjs.org');
  };

  this.setName = function(name) {
    this.nameInput.sendKeys(name);
  };
};
var AngularHomepage = require('./homepage.po.js');

describe('HomePage Tests', function(){
   var angularHomepage = new AngularHomepage();
   angularHomepage.nameInput.sendKeys('world');
});

best practices for writing tests

  • Use Protractor's by.model, by.binding and by.repeater instead of by.className or other dom specific methods
  • Use page objects to make the tests easier to maintain
  • Separate specs into different files and use node's .require() to run them

summery

  • E2E tests don't replace unit tests but compliment them to create ultra safe production deployments
  • Protractor makes it easier to write and maintain E2E tests for angular apps
  • Try to ship a version to production 10 times a day or even more
  • Have fun (:

questions?

Thank you (:

E2E testing with Protractor

By Ronen Amiel

E2E testing with Protractor

  • 597