Testing - Part 4 Faking Dependencies

Overview

Time: 5min

In this module, we will learn to use a fake dependency to isolate our tests

Learning Outcomes

  • How to fake out a dependency
  • How to fake out a dependency with an inline fake copy of a dependency
  • How to Use a Jasmine spyOn() to intercept service calls
  • How to override the dependency injection system to pass in a fake when the real dependency is called
  • Hot to use the real service but intercept calls to it with a Jasmine spy

Code for the Completed Lesson

To obtain the code for the completed lesson execute the following git commands.

git clone 'https://firebootcamp:Angular2rocks!@firebootcamp.visualstudio.com/FireBootCamp.Angular/_git/firebootcamp-testing'

git branch -a

git checkout -q 04 

Refactor code to have a beforeEach block

Time: 10min

The Jasmine framework has many helpful methods for managing build up and tears down of code for each or all tests in a test suite. The beforeEach() method is the most commonly used helper to ensure before each test in a describe blocks suite of tests certain code is set up for the test like its dependencies.

  • Add a beforeEach block and move the creation of a new joke component into it so each test will have a new JokeComponent for each test.
  • Add a global component property to the top of the describe block to be used in the each test or spec

src/app/joke/joke.component.spec.ts

import { JokeComponent } from './joke.component';
describe(`Component: Joke Component`, () => {
    let component: JokeComponent;

    beforeEach(() => {
        component = new JokeComponent(null);
    });

    it(`should add 1 + 1`, () => {
        expect(1 + 1).toEqual(2);
    });

    it(`should have a title of 'Chuck Norris Jokes`, () => {
        expect(component.title).toEqual('Chuck Norris Jokes');
    });

});

Add fake jokeService to the beforeEach

Time: 15min

One of the main options you have for faking a dependency like a joke service is to have a fake implementation of the joke service inline in your spec file which implements only the methods you want to test with predictable return values to check. This approach is a very valid and many prefer to isolate their test from changes which might happen in the real implements. However many also prefer to exercise their real service code and only intercept the method calls, which means less refactoring of all the files with inline copies. Both are great approaches, and it is encouraged to try both and learn which work best or use them in combination.

  • Add jokeService property to top of the describe block
  • Add the jokeService fake to the beforeEach block

src/app/joke/joke.component.spec.ts

describe(`Component: Joke Component`, () => {
  let component: JokeComponent;
  let jokeService: any;

  beforeEach(() => {
    jokeService = {
      getJoke: () => Observable.of('FAKE JOKE')
    };

    component = new JokeComponent(jokeService);
  });



Add a spyOn() method to a new test to check the joke contents

Time: 15min

Jasmine has test double functions called spies. A spy can stub any function and tracks calls to it and all arguments. A spy only exists in the describe or a it block in which it is defined and will be removed after each spec. There are special matchers for interacting with spies- Add jokeService property to the top of the describe block. You can read more in the Jasmine docs here https://jasmine.github.io/2.5/introduction

  • Create a new it block to test the joke property is being set correctly on initialization of the component
  • Add a spyOn function. You can see this spy takes the service to fake and the method as a string as a second parameter of the method on the service to intercept. You can do many things when intercepting a call to the method like return a specific value, call through to the real function or passing a fake of your own function. In this case, we will return an Observable.of('Fake Service'), so we will need to import of from rxjs and to keep it easy we will import the whole library in one go import 'rxjs/Rx'; .
  • Call component.ngOnInit() to trigger the call to the service
  • Check if the components joke is set to the "FAKE JOKE" we return from the spyOn function

src/app/joke/joke.component.spec.ts


  it('should set the joke property when component intiialised', () => {
    spyOn(jokeService, 'getJoke')
      .and.returnValue(Observable.of('FAKE JOKE'));

    component.ngOnInit();

    expect(component.joke).toEqual('FAKE JOKE');
  });

Final Code

Time: 0min

src/app/joke/joke.component.spec.ts

import { JokeComponent } from './joke.component';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';

describe(`Component: Joke Component`, () => {
    let component: JokeComponent;
    let jokeService: any;

    beforeEach(() => {
        jokeService = {
            getJoke: () => Observable.of('FAKE JOKE')
        };

        component = new JokeComponent(jokeService);
    });

    it(`should add 1 + 1`, () => {
        expect(1 + 1).toEqual(2);
    });

    it(`should have a title of 'Chuck Norris Jokes`, () => {
        expect(component.title).toEqual('Chuck Norris Jokes');
    });

    it('should set the joke property when component intiialised', () => {
        spyOn(jokeService, 'getJoke')
            .and.returnValue(Observable.of('FAKE JOKE'));

        component.ngOnInit();

        expect(component.joke).toEqual('FAKE JOKE');
    });

});


Summary

Time: 5min

In this lesson, we looked how to fake a service and how to use a Jasmine spy. In the next lesson, we will look at How to pass in the real service using the TestBed API from Angular and spyOn this. We will also look at interacting with the component's template making a shallow test.