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. Unit Testing In Angular
    Wednesday April 4, 2018
    Doug Corbett
    [email protected]

    View Slide

  2. 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

    View Slide

  3. on
    What is Unit Testing
    and Why Should I Care?

    View Slide

  4. Unit Tests
    • Tests designed to prove software functionality works
    according to client specification.

    View Slide

  5. Unit Test Characteristics
    • Functionality tested is small and isolated per test.
    • Can be automated.

    View Slide

  6. One Extreme – The Purist
    “Unit Test everything or
    you are just a hack”
    * https://www.fjackets.com/buy/Sherlock-Holmes-Coat.html

    View Slide

  7. 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/

    View Slide

  8. 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

    View Slide

  9. 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.

    View Slide

  10. 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.

    View Slide

  11. Testing Breakout
    5%
    25%
    70%
    End-to-end
    Integration Testing
    Unit Testing

    View Slide

  12. Common Testing Frameworks

    View Slide

  13. Common Testing Frameworks

    View Slide

  14. on
    Anatomy of an Angular
    Unit Test

    View Slide

  15. Arrange, Act, Assert
    Set up the
    test scenario
    Do the work
    Test the
    results
    aka Given, When, Then

    View Slide

  16. 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);
    });
    });

    View Slide

  17. 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/);

    View Slide

  18. Jasmine Life Cycle Methods
    • beforeEach()
    • beforeAll()
    • afterEach()
    • afterAll()
    beforeEach(function() {
    // do some setup before each test
    });
    afterEach(function() {
    // do some cleanup after each test
    });

    View Slide

  19. 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

    View Slide

  20. 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);
    });
    });

    View Slide

  21. 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);
    });
    });

    View Slide

  22. 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);
    });
    });

    View Slide

  23. 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 {
    return [{ provide: CustomerRepository, useValue: this }];
    }
    }

    View Slide

  24. on
    Let’s Try It Out

    View Slide

  25. 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)

    View Slide

  26. 1040 EZ

    View Slide

  27. Demo

    View Slide

  28. on
    Code Coverage

    View Slide

  29. Code Coverage
    $ ng test –code-coverage
    Creates new folder “coverage”, from there open up index.html

    View Slide

  30. Final Thoughts

    View Slide

  31. Final Thoughts

    View Slide

  32. 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

    View Slide