AngularJS Testing Strategies
Upcoming SlideShare
Loading in...5
×
 

AngularJS Testing Strategies

on

  • 241 views

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

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

Statistics

Views

Total Views
241
Views on SlideShare
221
Embed Views
20

Actions

Likes
1
Downloads
5
Comments
0

1 Embed 20

https://twitter.com 20

Accessibility

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • TDD (TDD is dead) – could be a whole talk on its own <br /> Grunt / Karma – though they do provide great value <br />
  • Unit / e2e testing <br /> Strategies for testing <br /> Lessons learned <br /> Code <br /> examples <br />
  • Strategic reasons <br /> Helps us fail fast <br /> Safety net – confidence <br /> Helps us understand <br /> Tactical reasons <br /> Dynamically typed language with almost no help from compiler <br />
  • Strategic reasons <br /> Helps us fail fast <br /> Safety net – confidence <br /> Helps us understand <br /> Tactical reasons <br /> Dynamically typed language with almost no help from compiler <br />
  • Strategic reasons <br /> Helps us fail fast <br /> Safety net – confidence <br /> Helps us understand <br /> Tactical reasons <br /> Dynamically typed language with almost no help from compiler <br />
  • Strategic reasons <br /> Helps us fail fast <br /> Safety net – confidence <br /> Helps us understand <br /> Tactical reasons <br /> Dynamically typed language with almost no help from compiler <br />
  • Javascript can be hard <br />
  • What does this mean? <br /> - Separation concerns <br />
  • Need a new image this is terrible <br />
  • Pattern <br /> IOC <br />
  • Show something where dependencies are not injected <br />
  • DI in angular <br />
  • That the controller doesn’t need the dom <br />
  • Something that might be written in jquery <br />
  • Something that we might write to test that. <br /> Lacks readability and hard to understand. <br />
  • Maybe mention filters and directives <br /> Format expressions <br />
  • What do you use it for… <br /> Why would you use it… <br /> How do you use it? <br /> Behaviors vs. correctness <br /> brittleness <br />
  • Justin Searls <br />
  • Tests what’s important <br />
  • expect(greeting.getText()).toEqual(&apos;Hello &apos;Nate Peterson&apos;); <br />

AngularJS Testing Strategies AngularJS Testing Strategies Presentation 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 nohelp 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 way function AddCtrl($scope) { $scope.Calc = function() { $scope.result = $scope.operand1 + $scope.operand2; } }
  • Controllers - The Angular way var $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 testing and e2e testing
  • How much reality do you need in 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