ANGULARJS UNIT TESTING Hor Norin 7 November 2016
Objective & Goals • Why unit test? • Basic Setup • Jasmine • AngularJS Testing • Mocking & Stubbing • Best Practices
WHY UNIT TESTING?
Why Unit Testing? • Reduce bug • Improve code quality • Good documentation • Defend against other programmers • Make you feel confident
BASIC SETUP
Tools You Need • Node.js • Karma • Jasmine • Angular Mock • Browser
Package.json { "name": "jasmine", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "karma start" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "angular": "^1.5.8" }, "devDependencies": { "angular-mocks": "^1.5.8", "jasmine-core": "^2.5.2", "karma": "^1.3.0", "karma-jasmine": "^1.0.2", "karma-phantomjs-launcher": "^1.0.2" } }
Setup Karma • npm install karma-cli –g • karma init • karma start
Configuration Options • frameworks • files • exclude • browsers • basePath • preprocessors • singleRun • autoWatch
JASMINE
Functions • Suite: describe • Spec: it • Setup: beforeEach • Teardown: afterEach • Expectation: expect
Matchers expect(array).toContain(member); expect(fn).toThrow(string); expect(fn).toThrowError(string); expect(instance).toBe(instance); expect(mixed).toBeDefined(); expect(mixed).toBeFalsy(); expect(mixed).toBeNull(); expect(mixed).toBeTruthy(); expect(mixed).toBeUndefined(); expect(mixed).toEqual(mixed); expect(mixed).toMatch(pattern); expect(number).toBeCloseTo(number, decimalPlaces); expect(number).toBeGreaterThan(number); expect(number).toBeLessThan(number); expect(number).toBeNaN(); expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalledTimes(number); expect(spy).toHaveBeenCalledWith(...arguments);
Custom Matcher • Use addMatchers • Factory object with factory function • Return object with compare function • Result of compare must contains pass and message property
Custom Matcher Example:
ANGULARJS TESTING
Loading Module describe('App', function() { beforeEach(module(’App')); it('1 + 1 should equal 2', function() { // some expectations }); });
Controller describe('SomeController', function() { var $scope, $controller; beforeEach(function() { module('App'); inject(function($rootScope, _$controller_) { $scope = $rootScope.$new(); $controller = _$controller_; }); }); it('expect someModel to equal someValue', function() { var ctrl = $controller('SomeController', { $scope: $scope }); // $scope style expect($scope.someModel).toEqual(someValue); // controllerAs style expect(ctrl.someModel).toEqual(someValue); }); });
Component describe('SomeComponent', function() { var $componentController, $scope; beforeEach(function() { module('App'); inject(function($rootScope, _$componentController_) { $scope = $rootScope.$new(); $componentController = _$componentController_; }); }); it('expect someModel to equal someValue', function() { var component = $componentController('SomeComponent', null); expect(component.someModel).toEqual(someValue); }); });
Directive describe('Greeting', function() { var $scope, $compile; beforeEach(function() { module('App'); inject(function($rootScope, _$compile_) { $scope = $rootScope.$new(); $compile = _$compile_; }); }); it('should contain greeting text', function() { var el = $compile('<greeting name="Norin"></greeting>')($scope); // fire all watchers so that expression // like {{ 1 + 1 }} will be evaluate $scope.$digest(); expect(el.html()).toContain('Hello Norin'); }); });
Service describe('Calculator', function() { var $scope, calculator; beforeEach(function() { module('App'); inject(function($rootScope, _calculator_) { $scope = $rootScope.$new(); calculator = _calculator_; }); }); it('should return sum of two value', function() { expect(calculator.sum(10, 20)).toEqual(30); }); });
Filter describe('Reverse', function() { var $scope, $filter; beforeEach(function() { module('App'); inject(function($rootScope, _$filter_) { $scope = $rootScope.$new(); $filter = _$filter_; }); }); it('should make text reverse', function() { var reverseText = $filter('reverse')('Hello'); expect(reverseText).toEqual('olleH'); }); });
MOCKING & STUBBING
Stub Object Method spyOn(someObj, 'someMethod').and.returnValue(3); expect(someObj.someMethod()).toEqual(3); spyOn(someObj, 'someMethod').and.callFake(function() { return 10; }); expect(someObj.someMethod()).toEqual(10);
Message Expectation var norin = { info: function() { return 'Name: Norin, Gender: Male'; } }; function greeting(person) { console.log(person.info()); } spyOn(norin, 'info'); greeting(norin); expect(norin.info).toHaveBeenCalled(); expect(norin.info.calls.count()).toBe(1);
Mock API Request $httpBackend.when('GET', 'http://example.com/search?q=product1').respond(200, [1,2]); var products = null; expect(products).toBeNull(); productService.search('product1') .then(function(res) { products = res.data; }) .catch(function(error) { products = []; }); $httpBackend.flush(); expect(products).not.toBeNull(); expect(products.length).toBe(2);
BEST PRACTICES
Things to Be Done • Test case should be done in isolation • Make use of custom matcher • Reactor out code if need
Things to Avoid • Directly use global variable & function • Nest describe block too deep • DRY too much makes it less readable
RESOURCES & REFERENCES
Resources & References • https://jasmine.github.io/2.0/introduction • https://docs.angularjs.org/api/ngMock
Further Reading • https://docs.angularjs.org/guide/e2e-testing • https://github.com/angular/protractor

AngularJS Unit Testing

Editor's Notes

  • #2 This template can be used as a starter file for presenting training materials in a group setting. Sections Sections can help to organize your slides or facilitate collaboration between multiple authors. On the Home tab under Slides, click Section, and then click Add Section. Notes Use the Notes pane for delivery notes or to provide additional details for the audience. You can see these notes in Presenter View during your presentation. Keep in mind the font size (important for accessibility, visibility, videotaping, and online production) Coordinated colors Pay particular attention to the graphs, charts, and text boxes. Consider that attendees will print in black and white or grayscale. Run a test print to make sure your colors work when printed in pure black and white and grayscale. Graphics, tables, and graphs Keep it simple: If possible, use consistent, non-distracting styles and colors. Label all graphs and tables.
  • #3 Give a brief overview of the presentation. Describe the major focus of the presentation and why it is important. Introduce each of the major topics. To provide a road map for the audience, you can repeat this Overview slide throughout the presentation, highlighting the particular topic you will discuss next.
  • #4 Use a section header for each of the topics, so there is a clear transition to the audience.
  • #5 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #6 Use a section header for each of the topics, so there is a clear transition to the audience.
  • #7 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #8 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #9 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #10 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #11 Use a section header for each of the topics, so there is a clear transition to the audience.
  • #12 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #13 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #14 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #15 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #16 Use a section header for each of the topics, so there is a clear transition to the audience.
  • #17 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #18 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #19 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #20 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #21 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #22 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #23 Use a section header for each of the topics, so there is a clear transition to the audience.
  • #24 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #25 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #26 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #27 Use a section header for each of the topics, so there is a clear transition to the audience.
  • #28 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #29 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #30 Use a section header for each of the topics, so there is a clear transition to the audience.
  • #31 Keep it brief. Make your text as brief as possible to maintain a larger font size.
  • #32 Keep it brief. Make your text as brief as possible to maintain a larger font size.