angularjs testing strategies

Post on 10-May-2015

608 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

TRANSCRIPT

AngularJS testing

strategies

Nate Peterson@njpetersonPa

What’s not in this talk

What’s this talk about

Why do we care about testing?

Tests help us fail fast

Tests give us confidence

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

JavaScript is a dynamically typed

language with almost no help from compiler

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

it is built with testing in mind.”

function MyController() {

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

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

The Angular way

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

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();

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

Controllers - The Angular wayvar $scope = {};

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

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

Two types of testing that

compliment each other

unit testingand

e2e testing

How much reality do you needin your tests?

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

Test all the things

is not a strategy

“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

Focus on behaviors rather than implementation details

Example – Testing a simple controller

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

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

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

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 }); }));});

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); })});

 

Example – mocking $http

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

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

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

});

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; })); });

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([{}, {}, {}]); })); });});

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 });

}); });});

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(); }); });});

 

Example - mocking services

 

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

 

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

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

});

 

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'); });});

 

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'); });});

 

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'); });});

 

Example – testing a directive

 

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

 

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() { });});

 

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() { });});

 

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() { });});

 

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'); });});

 

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'); });});

e2e testing / protractor

<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>

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>

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>

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>

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>

What’s worked well so far

Use of a Mock API for e2e tests

What’s been hard

Bloated controllers that lead to

bloated specs

Complicated unit test setup

Hard to read tests

Questions?

AngularJS testing

strategies

Nate Peterson@njpetersonPa

top related