Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unit Testing in Angular

Unit Testing in Angular

Unit Testing provides the foundation for solid application development. How do you know the code you wrote solves the problem the business asked you to solve? What about when functionality is complex? And how do you protect your code from subsequent changes? How do you know when you didn't break anything in the process of adding new code?

Answer: automated testing

This is the first in a two-part presentation about testing Angular code. We discuss the fundamentals of unit testing and how it fits in with other types of testing. We will create unit tests for Angular services, components and forms with Jasmine and Karma. We will also use spies and demonstrate code coverage analysis.

This presentation will set the stage for a subsequent presentation when we will discuss integration and end-to-end testing with more advanced techniques.

Doug Corbett

April 04, 2018
Tweet

More Decks by Doug Corbett

Other Decks in Programming

Transcript

  1. What is Unit Testing and Why Should I Care? 1

    Let’s Try it Out 3 Final Thoughts 4 Anatomy of an Angular Unit Test 2 Agenda
  2. One Extreme – The Purist “Unit Test everything or you

    are just a hack” * https://www.fjackets.com/buy/Sherlock-Holmes-Coat.html
  3. Another Extreme “I don’t always test my code, but when

    I do – I do it in production” * https://legendsbeard.com/b/bearded-legend-chuck-norris/ http://www.xento.com/blog/unit-testing-and-its-importance-2/
  4. Case For Implementing Unit Tests * https://programmingwithmosh.com/csharp/unit-testing/ • Manual regression

    testing is error-prone, time consuming and often not thorough despite the best intentions • Unit testing is pretty easy to do. • Unit testing provides self documenting code. • Unit testing is fast and as thorough as you make it. • TDD allows you to write tests that clearly describes business intent • No one knows the code as well as the developers
  5. Case Against Implementing Unit Tests * https://programmingwithmosh.com/csharp/unit-testing/ • Seems like

    a waste of time. • Some developers think testing is beneath them. • Eyeballing test results during development is sufficient • Not enough time to test every permutation, so why do any. • Current team doesn’t use unit tests.
  6. So When do I Recommend Unit Tests? On projects where

    there is business logic or validation. So yeah … All projects The key is to keep the tests small and test paths that are particularly complicated or error prone.
  7. Arrange, Act, Assert Set up the test scenario Do the

    work Test the results aka Given, When, Then
  8. Jasmine Keywords and Concepts • describe - means of organizing

    tests • it - start of a test • xit - skip test • expect - assertions • toBe - matcher describe("A suite", function() { it("contains spec with an expectation", function() { expect(true).toBe(true); }); });
  9. Jasmine – Other Matchers expect(true).toBe(true); expect(false).not.toBe(true); expect(a).toEqual(12); expect(message).toMatch(/bar/); expect(message).toMatch("bar"); expect(a.foo).toBeDefined();

    expect(a.bar).toBeUndefined() expect(a).toBeNull() expect(foo).toBeTruthy(); expect(a).toBeFalsy(); expect(a).toContain("bar") expect(e).toBeLessThan(pi); expect(pi).toBeGreaterThan(e); expect(pi).toBeCloseTo(e, 0); expect(bar).toThrow(); expect(foo).toThrowError("foo bar baz"); expect(foo).toThrowError(/bar/);
  10. Jasmine Life Cycle Methods • beforeEach() • beforeAll() • afterEach()

    • afterAll() beforeEach(function() { // do some setup before each test }); afterEach(function() { // do some cleanup after each test });
  11. Unit Test Concepts • Stubs – can substitute a class

    method or property with itself • Mocks – A full blown class with the same signature as a class it is intended to replace. • Spies – a type of mock that can simulate return values and count the number of times a method was call and with which parameters. Relies on SpyObject
  12. Stubs describe(“transfer", function() { it(“should return true when from account

    has sufficient funds", function() { // arrange let customerRepository = { getAccount: function(string accountNumber) { if (accountNumber == 1) { return { “accountNumber”: accountNumber, balance: 302.23 }; if (accountNumber == 2) { return { “accountNumber”: accountNumber, balance: 743.90 }; return null; } }; let bankService: BankService = new BankService(customerRepository); string fromAcct = ‘544325667’; string toAcct = ‘123123123’; decimal transferAmount = 100; // act let result = bankService.Transfer(fromAcct, toAcct, transferAmount); // assert expect(result).toBe(true); }); });
  13. Mocks class MockCustomerRepository: ICustomerRepository { public getAccount(string accountNumber) { if

    (accountNumber == 1) { return { “accountNumber”: accountNumber, balance: 302.23 }; } if (accountNumber == 2) { return { “accountNumber”: accountNumber, balance: 743.90 }; return null; } public getTop10Customers() { … } public requestBackgroundCheck(string customerID) { … } } describe(“transfer", function() { it(“should return true when from account has sufficient funds", function() { // arrange let customerRepository = new MockCustomerRepository(); let bankService: BankService = new BankService(customerRepository); string fromAcct = ‘544325667’; string toAcct = ‘123123123’; decimal transferAmount = 100; // act let result = bankService.Transfer(fromAcct, toAcct, transferAmount); // assert expect(result).toBe(true); }); });
  14. Spies describe(“transfer", function() { it(“should call getAccount twice when transferring

    funds", function() { // arrange let repo = new CustomerRepository(); let spy = spyOn(repo, ‘getAccount’).and.returnValue( new Account() {“accountNumber”: ‘544325667’, “balance”: 3432.98 }); let bankService: BankService = new BankService(repo); string fromAcct = ‘544325667’; string toAcct = ‘123123123’; decimal transferAmount = 100; // act let result = bankService.Transfer(fromAcct, toAcct, transferAmount); // assert expect(spy).toHaveBeenCalledTimes(2); }); });
  15. Spies 2 describe(“transfer", function() { it(“should call getAccount twice when

    transferring funds", function() { // arrange let repo= new CustomerRepositorySpy(); let bankService: BankService = new BankService(spy); string fromAcct = ‘544325667’; string toAcct = ‘123123123’; decimal transferAmount = 100; // act let result = bankService.Transfer(fromAcct, toAcct, transferAmount); // assert expect(spy.getAccountSpy).toHaveBeenCalledTimes(2); }); }); import { SpyObject } from ‘./test.helpers’; import {CustomerRepository } from ‘./customer.repository’; export class CustomerRepositorySpy: ICustomerRepository extends SpyObject { getAccountSpy; constructor() { super(CustomerRepository); this.getAccountSpy = this.spy(‘getAccount’).and.returnValue new Account() {“accountNumber”: ‘544325667’, “balance”: 3432.98 }); } getProviders(): Array<any> { return [{ provide: CustomerRepository, useValue: this }]; } }
  16. Scenario The IRS called and they want you to build

    a web page to help citizens donate their hard earned money to fund government projects. They call it ez1040.gov. Some of the website is complete, we need you to do the following. 1. Create a tax service that takes a taxable amount and returns the tax owed or a refund amount with unit tests. (basic flow) 2. Create a SSN validator service that takes a name and ssn and calls a third party web service with unit tests. (stub, mock and Mock backend) 3. Make sure that when a user changes the taxable amount twice it triggers a call to getJointTax twice and getSingleTax zero times. (spy)
  17. Reference Materials Official Angular Documentation https://angular.io Jasmine Github Repo https://jasmine.github.io/

    Karma Github Repo https://karma-runner.github.io ng-book – The Complete Book on Angular 4 – Nathan Murray and Ari Lerner Angular Unit Test Demo code