Test Framework: Jasmine
Headless Browser: PhantomJS
Assertions: Chai
Mocking: Sinon
Perfect for Test Driven Development (TDD)
Principle: Testing in Isolation -> Dependencies are mocked
angular.module('app.mathService', []) .service('mathService', function() { this.add = function(a, b) { return a + b; }; }); describe('mathService', function () { var mathService; beforeEach(function () { module('app.mathService'); inject(function (_mathService_) { mathService = _mathService_; }); }); describe('add()', function() { it('adds two numbers', function() { expect(mathService.add(2, 3)).toEqual(5); }); }); });
angular.module('app.itemService', []) .service('itemService', function(validatorService) { this.update = function(item) { if (validatorService.isValid(item)) { // Update item return true; } else { return false; } }; });
describe('itemService', function () { var itemService, isValid; beforeEach(function () { module('app.itemService', function($provide) { $provide.constant('validatorService', { isValid: function() { return isValid; } }); }); inject(function (_itemService_) { itemService = _itemService_; }); isValid = false; }); });
// $provide.constant('itemService', { // isValid: function() { return isValid; } // }); describe('update()', function() { it('updates when validation is passed', function() { isValid = true; expect(itemService.update({})).toBeTruthy(); }); it('does not update when validation fails', function() { isValid = false; expect(itemService.update({})).toBeFalsy(); }); });
angular.module('app.itemService', []) .service('itemService', function(validatorService) { this.update = function(item) { if (validatorService.isValid(item)) { // Update item return true; } else { return false; } }; });
describe('itemService', function () { var itemService, validatorService; beforeEach(function () { module('app.itemService'); inject(function (_itemService_, _validatorService_) { itemService = _itemService_; validatorService = _validatorService_; }); }); });
describe('update()', function() { var item = {}; it('updates when validation is passed', function() { spyOn(validatorService, 'isValid').and.returnValue(true); expect(validatorService.isValid).toHaveBeenCalledWith(item); expect(itemService.update(item)).toBeTruthy(); }); it('does not update when validation fails', function() { spyOn(validatorService, 'isValid').and.returnValue(false); expect(validatorService.isValid).toHaveBeenCalledWith(item); expect(itemService.update(item)).toBeFalsy(); }); });
Test interaction with UI
Integration Test
angular.module('app.incrementer', []). .directive('incrementer', function() { return { restrict: 'E', scope: { item: '=' }, template: '<button ng-click="increment()">+</button>', link: function(scope) { scope.increment = function() { item.count = item.count + 1; } } }; });
describe('incrementer', function () { var element, scope, item = {}; beforeEach(function () { item.count = 0; module('app.incrementer'); inject(function ($compile, $rootScope) { var $scope = $rootScope.$new(); $scope.item = item; template = '<incrementer item="item"></incrementer>'; element = angular.element(template); $compile(element)($scope); $scope.$digest(); scope = element.isolateScope(); }); }); });
it('calls increment on click', function () { spyOn(scope, 'increment'); element.find('button').click(); expect(scope.increment).toHaveBeenCalled(); }); describe('increment()', function() { it('increments the item\'s count property', function() { expect(item.count).toEqual(0); scope.increment(); expect(item.count).toEqual(1); }); });
Protractor: Based on Webdriver
Jasmine like syntax
describe('E2E Test', function () { beforeEach(function () { browser().navigateTo('http://localhost/url/to/test/index.html'); }); it("should show a dialog", function () { element('.show-dialog').click(); expect(element(".dialog").count()).toBe(1); }); });