angularjs testing strategies
Post on 10-May-2015
608 Views
Preview:
DESCRIPTION
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