angularjs testing strategies

64
AngularJS testing strategies Nate Peterson @njpetersonPa

Upload: njpst8

Post on 10-May-2015

599 views

Category:

Software


1 download

DESCRIPTION

A question of why we test, what makes AngularJS easy to test, and strategies to test AngularJS.

TRANSCRIPT

Page 1: AngularJS Testing Strategies

AngularJS testing

strategies

Nate Peterson@njpetersonPa

Page 2: AngularJS Testing Strategies

What’s not in this talk

Page 3: AngularJS Testing Strategies

What’s this talk about

Page 4: AngularJS Testing Strategies

Why do we care about testing?

Page 5: AngularJS Testing Strategies

Tests help us fail fast

Page 6: AngularJS Testing Strategies

Tests give us confidence

Page 7: AngularJS Testing Strategies

Tests help us understand what we’re doing and where we’re going

Page 8: AngularJS Testing Strategies

JavaScript is a dynamically typed

language with almost no help from compiler

Page 9: AngularJS Testing Strategies

“One of the fundamental reasons for choosing Angular is cited as that

it is built with testing in mind.”

Page 10: AngularJS Testing Strategies
Page 11: AngularJS Testing Strategies
Page 12: AngularJS Testing Strategies

function MyController() {

var dep1 = new Dep1(); var dep2 = new Dep2(); //do something with dep1 and dep2 ...}

Page 13: AngularJS Testing Strategies

someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) { ... $scope.aMethod = function() { ... } ... }]);

The Angular way

Page 14: AngularJS Testing Strategies
Page 15: AngularJS Testing Strategies

function AddCtrl() { var operand1 = $(#operand1); var operand2 = $(#operand2); var result = $(#result); this.add = function() { result = operand1 + operand2; }}

Page 16: AngularJS Testing Strategies

var operand1 = $('<input type="text" id="operand1" />');

var operand2 = $('<input type="text" id="operand1" />');

var result = $('<input type="text" id= "result" />');

var span = $('<span>'); $('body').html('<div class="ex1">') .find('div') .append(operand1) .append(operand2) .append(result); var ac = new AddCtrl(); operand1.val('1'); operand2.val('1'); ac.add(); expect(result.val()).toEqual('2'); $('body').empty();

Page 17: AngularJS Testing Strategies

Controllers - The Angular wayfunction AddCtrl($scope) { $scope.Calc = function() { $scope.result = $scope.operand1 + $scope.operand2; }}

Page 18: AngularJS Testing Strategies

Controllers - The Angular wayvar $scope = {};

var ctrl = $controller(‘AddCtrl’), {$scope: $scope };$scope.operand1 = 1;$scope.operand2 = 1; $scope.calc();

expect($scope.result).toEqual(2);

Page 19: AngularJS Testing Strategies

Two types of testing that

compliment each other

Page 20: AngularJS Testing Strategies

unit testingand

e2e testing

Page 21: AngularJS Testing Strategies

How much reality do you needin your tests?

Page 22: AngularJS Testing Strategies

Knowing what to test is just as important as how to test

Page 23: AngularJS Testing Strategies

Test all the things

is not a strategy

Page 24: AngularJS Testing Strategies

“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence…”

-- Kent Beck

Page 25: AngularJS Testing Strategies

Focus on behaviors rather than implementation details

Page 26: AngularJS Testing Strategies

Example – Testing a simple controller

Page 27: AngularJS Testing Strategies

app.controller('AddCtrl', function($scope) { $scope.calc = function() { $scope.result = $scope.operand1 + $scope.operand2; }});

Page 28: AngularJS Testing Strategies

app.controller('AddCtrl', function($scope) { $scope.calc = function() { $scope.result = $scope.operand1 + $scope.operand2; }});

describe('adding 1 + 1', function() { beforeEach(module('myApp')); });

Page 29: AngularJS Testing Strategies

app.controller('AddCtrl', function($scope) { $scope.calc = function() { $scope.result = $scope.operand1 + $scope.operand2; }});

describe('adding 1 + 1', function() { beforeEach(module('myApp')); var ctrl, scope; beforeEach(inject(function($controller, $rootScope) { scope = $rootScope.$new(); ctrl = $controller('AddCtrl', { $scope: scope }); }));});

Page 30: AngularJS Testing Strategies

app.controller('AddCtrl', function($scope) { $scope.calc = function() { $scope.result = $scope.operand1 + $scope.operand2; }});

describe('adding 1 + 1', function() { beforeEach(module('myApp')); var ctrl, scope; beforeEach(inject(function($controller, $rootScope) { scope = $rootScope.$new(); ctrl = $controller('AddCtrl', { $scope: scope }); }));

it('should equal 2', function() { scope.operand1 = 1; scope.operand2 = 1; scope.calc();

expect(scope.result).toEqual(2); })});

Page 31: AngularJS Testing Strategies

 

Example – mocking $http

Page 32: AngularJS Testing Strategies

var app = angular.module('myApp', []);

app.controller('MoviesController', function($scope, $http) { $http.get("/api/movies") .then(function (result) { $scope.movies = result.data; });});

Page 33: AngularJS Testing Strategies

describe("myApp", function () { beforeEach(module('myApp')); describe("MoviesController", function () { var scope, httpBackend, http, controller;

});

Page 34: AngularJS Testing Strategies

describe("myApp", function () { beforeEach(module('myApp')); describe("MoviesController", function () { var scope, httpBackend, http, controller;

beforeEach(inject(function ($rootScope, $controller, $httpBackend, $http) {

scope = $rootScope.$new(); httpBackend = $httpBackend; http = $http; controller = $controller; })); });

Page 35: AngularJS Testing Strategies

describe("myApp", function () { beforeEach(module('myApp')); describe("MoviesController", function () { var scope, httpBackend, http, controller;

beforeEach(inject(function ($rootScope, $controller, $httpBackend, $http) {

scope = $rootScope.$new(); httpBackend = $httpBackend; http = $http; controller = $controller; httpBackend.when("GET", "/api/movies") .respond([{}, {}, {}]); })); });});

Page 36: AngularJS Testing Strategies

describe("myApp", function () { beforeEach(module('myApp')); describe("MoviesController", function () { var scope, httpBackend, http, controller;

beforeEach(inject(function ($rootScope, $controller, $httpBackend, $http) {

scope = $rootScope.$new(); httpBackend = $httpBackend; http = $http; controller = $controller; httpBackend.when("GET", "/api/movies") .respond([{}, {}, {}]); }));

it('should GET movies', function () { httpBackend.expectGET('/api/movies'); controller('MoviesController', { $scope: scope, $http: http });

}); });});

Page 37: AngularJS Testing Strategies

describe("myApp", function () { beforeEach(module('myApp')); describe("MoviesController", function () { var scope, httpBackend, http, controller;

beforeEach(inject(function ($rootScope, $controller, $httpBackend, $http) {

scope = $rootScope.$new(); httpBackend = $httpBackend; http = $http; controller = $controller; httpBackend.when("GET", "/api/movies") .respond([{}, {}, {}]); }));

it('should GET movies', function () { httpBackend.expectGET('/api/movies'); controller('MoviesController', { $scope: scope, $http: http }); httpBackend.flush(); }); });});

Page 38: AngularJS Testing Strategies

 

Example - mocking services

Page 39: AngularJS Testing Strategies

 

angular.module('myApp', []) .factory('greeter', function () { return 'Hello';}).factory('worldGreeter', function (greeter) { return greeter + ' World';});

Page 40: AngularJS Testing Strategies

 

angular.module('myApp', []) .factory('greeter', function () { return 'Hello';}).factory('worldGreeter', function (greeter) { return greeter + ' World';});

describe('worldGreeter', function () { beforeEach(inject(function (_worldGreeter_) { worldGreeter = _worldGreeter_; }));

});

Page 41: AngularJS Testing Strategies

 

angular.module('myApp', []) .factory('greeter', function () { return 'Hello';}).factory('worldGreeter', function (greeter) { return greeter + ' World';});

describe('worldGreeter', function () { beforeEach(inject(function (_worldGreeter_) { worldGreeter = _worldGreeter_; }));

it('should work with mocked greeter', function () { expect(worldGreeter).toEqual('WAT World'); });});

Page 42: AngularJS Testing Strategies

 

angular.module('myApp', []) .factory('greeter', function () { return 'Hello';}).factory('worldGreeter', function (greeter) { return greeter + ' World';});

describe('worldGreeter', function () {

beforeEach(module('myApp', function($provide) { $provide.value('greeter', 'WAT'); })); beforeEach(inject(function (_worldGreeter_) { worldGreeter = _worldGreeter_; }));

it('should work with mocked greeter', function () { expect(worldGreeter).toEqual('WAT World'); });});

Page 43: AngularJS Testing Strategies

 

angular.module('myApp', []) .factory('greeter', function () { return 'Hello';}).factory('worldGreeter', function (greeter) { return greeter + ' World';});

describe('worldGreeter', function () {

beforeEach(module('myApp', function ($provide) { $provide.decorator('greeter', function ($delegate) { return 'WAT'; }); })); beforeEach(inject(function (_worldGreeter_) { worldGreeter = _worldGreeter_; }));

it('should work with mocked greeter', function () { expect(worldGreeter).toEqual('WAT World'); });});

Page 44: AngularJS Testing Strategies

 

Example – testing a directive

Page 45: AngularJS Testing Strategies

 

var app = angular.module('myApp', []);app.directive('simpleDirective', function (){ return { restrict: 'E', template: '<div>{{value}}</div>', scope: { value: '=' } };});

Page 46: AngularJS Testing Strategies

 

describe('Testing simpleDirective', function() { var scope, elem, directive, compiled, html; beforeEach(function () { module('myApp‘); }); });

it('Should set the text of the element to whatever was passed.', function() { });});

Page 47: AngularJS Testing Strategies

 

describe('Testing simpleDirective', function() { var scope, elem, directive, compiled, html; beforeEach(function (){ module('myApp'); html = '<simple-directive value="abc"></simple-directive>'; });

it('Should set the text of the element to whatever was passed.', function() { });});

Page 48: AngularJS Testing Strategies

 

describe('Testing simpleDirective', function() { var scope, elem, directive, compiled, html; beforeEach(function (){ module('myApp'); html = ‘<simple-directive value="abc"></simple-directive>'; inject(function($compile, $rootScope) { scope = $rootScope.$new(); elem = angular.element(html); compiled = $compile(elem); compiled(scope); }); });

it('Should set the text of the element to whatever was passed.', function() { });});

Page 49: AngularJS Testing Strategies

 

describe('Testing simpleDirective', function() { var scope, elem, directive, compiled, html; beforeEach(function (){ module('myApp'); html = ‘<simple-directive value="abc"></simple-directive>'; inject(function($compile, $rootScope) { scope = $rootScope.$new(); elem = angular.element(html); compiled = $compile(elem); compiled(scope); }); });

it('Should set the text of the element to whatever was passed.', function() { expect(elem.text()).toBe('blah'); });});

Page 50: AngularJS Testing Strategies

 

describe('Testing simpleDirective', function() { var scope, elem, directive, compiled, html; beforeEach(function (){ module('myApp'); html = ‘<simple-directive value="abc"></simple-directive>'; inject(function($compile, $rootScope) { scope = $rootScope.$new(); elem = angular.element(html); compiled = $compile(elem); compiled(scope); }); });

it('Should set the text of the element to whatever was passed.', function() { scope.abc = 'blah'; scope.$digest();

expect(elem.text()).toBe('blah'); });});

Page 51: AngularJS Testing Strategies

e2e testing / protractor

Page 52: AngularJS Testing Strategies

<body> <h1>Sample</h1> <div> Two Way Data Binding Sample <br/><br/> <input type="text" ng-model="name" /> <span ng-show="name"><h4>Hello {{name}}</h4></span> </div> </body>

Page 53: AngularJS Testing Strategies

describe(demo page', function() { it('should greet the user', function() { browser.get('[some route]'); }); });

<body> <h1>Sample</h1> <div> Two Way Data Binding Sample <br/><br/> <input type="text" ng-model="name" /> <span ng-show="name"><h4>Hello {{name}}</h4></span> </div> </body>

Page 54: AngularJS Testing Strategies

describe(demo page', function() { it('should greet the user', function() { browser.get('[some route]'); element(by.model('name')).sendKeys('Nate Peterson'); }); });

<body> <h1>Sample</h1> <div> Two Way Data Binding Sample <br/><br/> <input type="text" ng-model="name" /> <span ng-show="name"><h4>Hello {{name}}</h4></span> </div> </body>

Page 55: AngularJS Testing Strategies

describe(demo page', function() { it('should greet the user', function() { browser.get('[some route]'); element(by.model('name')).sendKeys('Nate Peterson'); var greeting = element(by.binding('name')); }); });

<body> <h1>Sample</h1> <div> Two Way Data Binding Sample <br/><br/> <input type="text" ng-model="name" /> <span ng-show="name"><h4>Hello {{name}}</h4></span> </div> </body>

Page 56: AngularJS Testing Strategies

describe(demo page', function() { it('should greet the user', function() { browser.get('[some route]'); element(by.model('name')).sendKeys('Nate Peterson'); var greeting = element(by.binding('name')); expect(greeting.getText()).toEqual('Hello 'Nate Peterson');

}); });

<body> <h1>Sample</h1> <div> Two Way Data Binding Sample <br/><br/> <input type="text" ng-model="name" /> <span ng-show="name"><h4>Hello {{name}}</h4></span> </div> </body>

Page 57: AngularJS Testing Strategies

What’s worked well so far

Page 58: AngularJS Testing Strategies

Use of a Mock API for e2e tests

Page 59: AngularJS Testing Strategies

What’s been hard

Page 60: AngularJS Testing Strategies

Bloated controllers that lead to

bloated specs

Page 61: AngularJS Testing Strategies

Complicated unit test setup

Page 62: AngularJS Testing Strategies

Hard to read tests

Page 63: AngularJS Testing Strategies

Questions?

Page 64: AngularJS Testing Strategies

AngularJS testing

strategies

Nate Peterson@njpetersonPa