Angular.js

Grunt.js

Yeoman

Node.js

Bower.js

E2E Testing with Protractor

Lecture 11

Lab Ten

Solution

E2E Testing

What is E2E?

E2E stands for End-to-End

E2E testing is a methodology used to test whether the flow of an application is performing as designed from start to finish.

E2E tests validate the functionality of the application as a whole instead of the individual parts and pieces (unit testing).

In web applications, E2E testing typically automates the actions of an end user.

The preferred tool used to accomplish this testing in AngularJS applications is Protractor.

Protractor

What is Protractor?

Protractor is an end-to-end test framework for AngularJS applications thats built on top of WebDriverJS.

WebDriverJS is just a JavaScript API for Selenium.

What is Selenium?

Selenium is a tool that automates browsers; it runs as an independent server.

Think of Selenium as an automated end user.

We use WebDriverJS to send instructions to a Selenium server running in the background.

ChromeDriver

In this lab we're going to use ChromeDriver, which allows us to bypass a Selenium installation and "drive" Chrome directly.

Writing E2E Tests

Protractor and WebDriverJS expose global APIs that you can use to automate actions that a user would perform.

One important thing to note about Protractor tests is that, unlike Karma tests that run in the browser, Protractor tests run in Node.js and "drive" a browser.

E2E Test Syntax

Protractor uses the Jasmine test framework by default, so you can use the describe and it structure, just like in Karma tests.

The browser global variable is a wrapper around an instance of WebDriver.

You can use this for navigation and page-wide information.

The element global variable contains a helper function for finding and interacting with elements on the page you are testing.

The by global variable contains a collection of element locator strategies.

For example, elements can be found by CSS selector, by ID, or by the attribute they are bound to with ng-model.

These locator strategies are Angular-aware.

You can use them to find and interact with elements.

  • by.repeater
  • by.model
  • by.binding

protractor is the protractor namespace which wraps the webdriver namespace.

This contains static variables and classes, such as protractor.Key, which enumerates the codes for special keyboard signals.

We can use these functions to automate the end user and test that the page behaves in the way we expect.



describe('Employees', function () {

  beforeEach (function () {
    browser.get('#/app/employees');
  });
  
  describe("clicking new employee", function () {
    it('takes us to the create employee form', function () {
      element(by.buttonText("New Employee")).click();
      expect(element(by.binding('$state.current.data.section')).getText()).toBe("Create Employee");
    });
  });
  
});


Page Objects

When writing real tests scripts for your page, it's best to use the Page Objects pattern to make your tests more readable.

Page Objects

What are Page Objects?


  • Page Objects are models of the UI you are testing.
  • They reduce the amount of duplications in our tests required to interact with the user interface.
  • Page Objects "face" in both directions. You can use them to set or get things like field values.

Facing "toward" the developer of a test, they represent the services offered by a particular page.

Facing "away" from the developer, they should be the only thing that has a deep knowledge of the structure of the HTML of a page (or part of a page).

It's simplest to think of the methods on a Page Object as offering the "services" that a page offers rather than exposing the details and mechanics of the page.

Here we're creating a page object that models part of the employees list page.



function EmployeesIndexPage() {
  this.get = function () {
    browser.get('#/app/employees');
  };
  
  this.newEmployeeButton = element(
    by.buttonText("New Employee")
  );
  
  // Here, we're finding an element by its repeater configuration
  this.firstEmployee = element(
    by.repeater('employee in pageConfig.data').row(0)
  );
}

module.exports = new EmployeesIndexPage();


To start using a page object, just require it.



describe('Employees', function () {

  var indexPage = require('./index.page.js');
  
});


Then, we'll use a beforeEach to make sure that each of our specs start at the employee index page.



describe('Employees', function () {

  var indexPage = require('./index.page.js');
  
  beforeEach (function () {
    indexPage.get();
  });
  
});


Finally, we'll use the page object to perform actions and expect results.



describe('Employees', function () {

  var indexPage = require('./index.page.js'),
    formPage = require('./form.page.js');
    
  beforeEach (function () {
    indexPage.get();
  });
  
  describe("clicking new employee", function () {
    it('takes us to the create employee form', function () {
      indexPage.clickNewEmployee();
      expect(formPage.getPageTitle()).toBe("Create Employee");
    });
  });
  
});


Yes, it's that easy.

Things to consider

You should write tests that address the behavior of your application and not the framework (don’t test data binding).

Each test block should test as little as possible.

Lab Eleven

Protractor

In this lab, you'll set up Protractor and create some E2E tests.