#codefront2014

Full Spectrum Testing

By @CarmenPopoviciu and @PascalPrecht

codefront.io - May 10, 2014

Hi there.

We are...

@CarmenPopoviciu
@PascalPrecht

We have 40 min to cover full spectrum testing

YAY...

Unfortunately there's too much to cover. :(

But we can inspire you! :)

How do you like testing?

Why people don't test

Why people should test

So what do we cover today?

We inspire you with these

Unit Testing

E2E Testing

So next time you deploy an app, you don't feel like this...

But rather like this...

Ready to visit the land of milk and honey?

Let's go!

Unit Testing

What?

Why?

When?

Before or while the 'units' are being developed.

Tools

Karma is a test runner, that makes automated testing simpler and faster ...

as in VERY fast!

Pre-Karma Solutions

Goals

  • Testing on real browsers
  • Remote control
  • Speed
  • Integration with IDEs and text editors
  • Integration with CI servers
  • Extensibility
  • Debugging

Design

Client-server architecture with a bi-directional communication channel between the client and the server

The server is the master part of the system and runs on Node.js on the local machine.

The client is the place where all the tests are actually executed.

Demo

Configuration

module.exports = function(config) {
  config.set({
    basePath: '../..',
    files: [
        'app/scripts/**/*.js',
        'test/unit/*.spec.js'
    ],
    exclude: [],
    port: 1234,
    frameworks: ['jasmine'],
    autoWatch: true,
    browsers: ['Chrome', 'Safari', 'Firefox']
  });
};

behavior-driven development framework for testing JavaScript code

suites, specs and expectations

describe("A suite", function() {
   it("contains spec with an expectation", function() {
     expect(true).toBe(true);
   });
});

ngMock

Provides some useful mocking tools to test Angular modules

angular.mock.module, angular.mock.inject, $httpBackend

Examples

var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope', function($scope){
    $scope.tweets = [{
        text: 'Hello codefront.io'
    },{
        text: 'codefront.io rocks!'
    }];
}]);
describe("Controller: MyCtrl", function() {
    beforeEach(module('myApp'));

    it("should contain a tweets object on the scope",
        inject(function($rootScope, $controller) {
            var scope = $rootScope.$new();
            var ctrl = $controller('MyCtrl', {'$scope': scope});
            expect(scope.tweets).toBeDefined();
            expect(scope.tweets.length).toEqual(2);
    }));
});

describe("Controller: MyCtrl", function() {
    var scope;
    var ctrl;

    beforeEach(module('myApp'));

    beforeEach(inject(function($rootScope, $controller) {
        scope = $rootScope.$new();
        ctrl = $controller('MyCtrl', {
            '$scope': scope
        });
    }));

    it("should contain a tweets object on the scope", function() {
            expect(scope.tweets).toBeDefined();
            expect(scope.tweets.length).toEqual(2);
    });
});

app.service('twitter', TwitterService);

app.controller('MyCtrl', ['$scope', 'twitter', function($scope, twitter){
    $scope.tweets = twitter.tweets();
}]);
function TwitterService() {
    this.tweets = function() {
        // for the sake of the example
        var tweets = [{text: 'codefront.io rocks!'}];
        return tweets;
    };
}

describe("Controller: MyCtrl", function() {
      var scope;
      var ctrl;
      var mockService;

      beforeEach(module('myApp'));
      beforeEach(inject(function($rootScope, $controller) {
          scope = $rootScope.$new();
          mockService = {
              tweets: function(){
                  return [];
              }
          };
          ctrl = $controller('MyCtrl', {'$scope': scope,
              'twitter': mockService
          });
      }));

      it("should contain a tweets object on the scope", function() {
              expect(scope.tweets).toBeDefined();
              expect(scope.tweets.length).toEqual(0);
      });
});

E2E Testing

What?

Why?

Imagine you have to the following things by hand to test your app:

When?

Protractor is an E2E testing framework built on top of Webdriver to make E2E testing for Angular applications more fun.

Key features

Why not just Webdriver?

AngularJS gives structure to an app your runner can use:

Design

Demo

Writing tests (WebdriverJS)

var wd = require('selenium-webdriver');

var driver = new wd.Builder()
  .withCapabilities(wd.Capabilities.chrome())
  .build();

driver.get('some/url');
driver.findElement(wd.By.name('foo')).sendKeys('webdriver');
driver.findElement(wd.By.name('btn')).click();
driver.sleep(400);
driver.getTitle().then(function (title) {
  // do something
});

Wait!

Everything returns a promise, right?

Shouldn't my code look like this?

var wd = require('selenium-webdriver');

var driver = new wd.Builder()
  .withCapabilities(wd.Capabilities.chrome())
  .build();

driver.get('some/url').then(function () {
  driver.findElement(wd.By.name('foo')).then(function (element) {
    element.sendKeys('webdriver').then(function () {
      driver.findElement(wd.By.name('btn')).then(
        function (element2) {
          element2.click();
          // ...
          

Webdriver handles control flow!

var wd = require('selenium-webdriver');

var driver = new wd.Builder()
  .withCapabilities(wd.Capabilities.chrome())
  .build();

driver.get('some/url');
driver.findElement(wd.By.name('foo')).sendKeys('webdriver');
driver.findElement(wd.By.name('btn')).click();
driver.sleep(400);
driver.getTitle().then(function (title) {
  // do something
});

Writing tests (Protractor/Jasmine)

describe('something', function () {

  beforeEach(function () {
    browser.get('some/url');
  });

  it('should do something', function () {
    var foo = element(by.className('foo'));
    expect(foo.isDisplayed()).toBe(true);
  });

  it('should do something', function () {
    element(by.input('search')).sendKeys('bar');
    element(by.buttonText('submit'));
    expect(element(by.className('search-result')).isDisplayed())
      .toBe(true);
  });
});

Locator Strategies

<span>{{yourName}}</span> element(by.binding('yourName'));
<input ng-model="password"> element(by.input('password'));
<tr ng-repeat="foo in foos"> element(by.repeater('foo in foos')) .row(1)
element.by(repeater('foo in foos')) .column('foo.bar');

Page Objects

var TwitterApp = function () {
  this.search = function () {
    return element(by.classModel('search'));
  };
  this.tweets = function () {
    return element(by.repeater('tweet in tweets'));
  };
};
module.exports = new TwitterApp();
var app = require('./twitter-app.page.js');

it('should do something', function () {
  app.search().sendKeys('codefrontio');
  // ...
});

Debugging

Pausing to debug

it("contains spec with an expectation", function() {
  browser.get('some/url');

  browser.debugger();

  expect(testWillFail).toBe(true);
});
$ protractor debug protractor.conf.js

Interactive file explorer

$ ./bin/elementexplorer.js my-web-app.com
> element(by.id('foobar')).getText()
> browser.get('http://codefront.io')

Demo

Thanks!

Full Spectrum Testing

By @CarmenPopoviciu and @PascalPrecht

codefront.io - May 10, 2014