#native_company# #native_desc#
#native_cta#

Angular Test Bed


The Angular Test Bed (ATB) is a higher level Angular Only testing framework that allows us to easily test behaviours that depend on the Angular Framework.

We still write our tests in Jasmine and run using Karma but we now have a slightly easier way to create components, handle injection, test asynchronous behaviour and interact with our application.

This lecture will be an introduction to the ATB and we will continue to use it for the rest of this section.

Learning Objectives

  • What is the ATB and how to use it.

  • When to use ATB vs. plain vanilla Jasmine tests.

Configuring

Let’s demonstrate how to use the ATB by converting the component we tested with plain vanilla Jasmine to one that uses the ATB.

import {TestBed, ComponentFixture} from '@angular/core/testing';
import {LoginComponent} from './login.component';
import {AuthService} from "./auth.service";

describe('Component: Login', () => {

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [AuthService]
    });
  });
});

In the beforeEach function for our test suite we configure a testing module using the TestBed class.

This creates a test Angular Module which we can use to instantiate components, perform dependency injection and so on.

We configure it in exactly the same way as we would configure a normal NgModule. In this case we pass in the LoginComponent in the declarations and the AuthService in the providers.

Fixtures and DI

Once the ATB is setup we can then use it to instantiate components and resolve dependencies, like so:

import {TestBed, ComponentFixture} from '@angular/core/testing';
import {LoginComponent} from './login.component';
import {AuthService} from "./auth.service";

describe('Component: Login', () => {

  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>; (1)
  let authService: AuthService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [AuthService]
    });

    // create component and test fixture
    fixture = TestBed.createComponent(LoginComponent); (2)

    // get test component from the fixture
    component = fixture.componentInstance; (3)

    // UserService provided to the TestBed
    authService = TestBed.get(AuthService); (4)

  });
});
1 fixture is a wrapper for a component and its template.
2 We create an instance of a component fixture through the TestBed, this injects the AuthService into the component constructor.
3 We can find the actual component from the componentInstance on the fixture.
4 We can get resolve dependencies using the TestBed injector by using the get function.

Note

Since the LoginComponent doesn’t have its own child injector the AuthService that gets injected in is the same one as we get from the TestBed above.

Test Specs

Now we’ve configured the TestBed and extracted the component and service we can run through the same test specs as before:

it('needsLogin returns true when the user has not been authenticated', () => {
  spyOn(authService, 'isAuthenticated').and.returnValue(false);
  expect(component.needsLogin()).toBeTruthy();
  expect(authService.isAuthenticated).toHaveBeenCalled();
});

it('needsLogin returns false when the user has been authenticated', () => {
  spyOn(authService, 'isAuthenticated').and.returnValue(true);
  expect(component.needsLogin()).toBeFalsy();
  expect(authService.isAuthenticated).toHaveBeenCalled();
});

When to Use ATB

We will continue to use ATB for the rest of this section because:

  • It allows us to test the interaction of a directive or component with its template.

  • It allows us to easily test change detection.

  • It allows us to test and use Angular’s DI framework.

  • It allows us to test using the NgModule configuration we use in our application.

  • It allows us to test user interaction via clicks and input fields

Summary

The ATB lets us test parts of our code as if it is being run in the context of a real Angular app.

Its usefulness will become more apparent in future lectures, the next one being how to use the ATB to test change detection and property binding.

Listing

Listing 1. login.component.spec.ts
/* tslint:disable:no-unused-variable */
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { LoginComponent } from './login.component';
import { AuthService } from "./auth.service";

describe('Component: Login', () => {

    let component: LoginComponent;
    let fixture: ComponentFixture<LoginComponent>;
    let authService: AuthService;

    beforeEach(() => {

        // refine the test module by declaring the test component
        TestBed.configureTestingModule({
            declarations: [LoginComponent],
            providers: [AuthService]
        });

        // create component and test fixture
        fixture = TestBed.createComponent(LoginComponent);

        // get test component from the fixture
        component = fixture.componentInstance;

        // UserService provided to the TestBed
        authService = TestBed.get(AuthService);

    });

    it('needsLogin returns true when the user has not been authenticated', () => {
        spyOn(authService, 'isAuthenticated').and.returnValue(false);
        expect(component.needsLogin()).toBeTruthy();
        expect(authService.isAuthenticated).toHaveBeenCalled();
    });

    it('needsLogin returns false when the user has been authenticated', () => {
        spyOn(authService, 'isAuthenticated').and.returnValue(true);
        expect(component.needsLogin()).toBeFalsy();
        expect(authService.isAuthenticated).toHaveBeenCalled();
    });
});

Caught a mistake or want to contribute to the book? Edit this page on GitHub!



Advanced JavaScript

This unique course teaches you advanced JavaScript knowledge through a series of interview questions. Bring your JavaScript to the 2021's today.

Level up your JavaScript now!