Get the "Migrating an Angular 1 App to Angular 2 book" for Free. Spread the word and download it now!

Components enable us to display content on our website. The Angular compiler parses component templates and displays them according to the given state. The business logic inside the components can be tested with traditional unit tests just like services. But what happens with the displayed HTML from the template and the interaction with other components? In this tutorial I'll show you how to test the rendered components, starting from isolated unit tests to fully integrated ones.

This article is the third part of a series in which I share my experiences testing different building blocks of an Angular application. It relies heavily on Dependency Injection based testing and it is recommended that you read the first part if you are not familiar with the concepts.

The component we'll test

The component we will test is a login form. It doesn't directly access the Authentication service, instead it just informs the parent component about the submission through an Output property. The form consists of two fields: an email and a password field. The email address can be preloaded through an Input property if it is passed down from the wrapping parent component.

Angular Testing Framework

The two fields are handled by a reactive form created in the component. With reactive forms, we can add validation to the fields and access their values without touching the DOM. I've chosen reactive forms because they are more flexible and easier to test than template-driven forms. Template-driven forms can only be tested through the DOM, making test writing harder.

If you are not familiar with reactive forms, it is recommended to read this part from the official documentation.

The FormBuilder is created inside the constructor and adds validation to the input fields. When the email input changes, the ngOnChanges lifecycle hook passes it down to the field. Setting the value on one of the forms control also updates its value in the HTML input element.

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

  selector: 'login-form',
  templateUrl: './login-form.template.html'
export class LoginFormComponent {

  private loginForm: FormGroup;

  constructor(private formBuilder: FormBuilder) {
    this.loginForm ={
      email: ['', [Validators.required,]],
      password: ['', Validators.required]

  email: string;

  submitted = new EventEmitter();

  ngOnChanges(change) {
    if ( {

  onSubmit({ email, password }) {
    this.submitted.emit({ email, password });

The template only includes tags that are necessary for the form to function. The value of the formControlName property will be the reference name when we access our inputs inside the form controls. With the [formGroup] property, we fire up the form handling with the components FormGroup and listen to form submissions with (ngSubmit). When we click on the submit button, Angular will catch the event and pass it to the onSubmit handler function.

<form (ngSubmit)="onSubmit(loginForm.value)" [formGroup]="loginForm">
    <input type="text" formControlName="email" id="login-email">

    <input type="password" formControlName="password" id="login-password">

    <button type="submit">Log In</button>

Isolated tests

If we just want to focus on the business logic, we can treat and test the component as a service. The Component decorator extends the Injectable decorator, which means it can be created as a service. We just have to pass the component to the providers array in the module dependencies. When testing in isolation, the template never gets compiled. It only gets the required dependencies through the constructor.

import { LoginFormComponent } from './login-form.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TestBed, inject, async } from '@angular/core/testing';
import { Component } from '@angular/core';

describe('Isolated', () => {
  let subject: LoginFormComponent;

  beforeEach(() => {
      providers: [LoginFormComponent],
      imports: [FormsModule, ReactiveFormsModule]

  beforeEach(inject([LoginFormComponent], (loginForm: LoginFormComponent) => {
    subject = loginForm;

  it('should send credentials on submit', () => {
    subject.submitted.subscribe(({ email, password }) => {

    subject.onSubmit({ email: expectedEmail, password: expectedPassword });

The test focuses on the method that is called when the form is submitted. It only passes the given email and password to the EventEmitter after destructuring the input object. We don't have to pass Jasmine's asynchronous done callback to the testcase because the EventEmitter acts synchronously.

Isolated tests are good when you want to focus on the component's logic. These tests are also much faster than any other solution. The only downsides are that it won't detect errors in the template nor check the interactions with other components.

Shallow tests

If we also want to detect errors inside the template, but still focus on a single component, shallow tests are the way to go. The key difference compared to isolated tests is that here the component is compiled.

Inside the beforeEach block, the component class moves to the declarations property instead of providers. Before we can create an instance of the component, it has to be compiled. The compileComponents method does the task asynchronously. It can't be synchronous, because templates and styles can be referenced with relative urls and the fetching of these resources is asynchronous by nature. We have to wait for these tasks to complete. We can do the waiting with the async helper function. In the background, async creates a new zone and waits until every asynchronous operation is finished within that zone. This way, we don't have to fiddle with Jasmine's done callback.

describe('Shallow', () => {
  beforeEach(async(() => {
      declarations: [LoginFormComponent],
      imports: [FormsModule, ReactiveFormsModule]

  it('should send credentials on submit', () => {
    let fixture = TestBed.createComponent(LoginFormComponent);
    let component: LoginFormComponent = fixture.componentInstance;
    let element = fixture.nativeElement;


    element.querySelector('#login-email').value = expectedEmail;
    element.querySelector('#login-email').dispatchEvent(new Event('input'));
    element.querySelector('#login-password').value = expectedPassword;
    element.querySelector('#login-password').dispatchEvent(new Event('input'));


    component.submitted.subscribe(({ email, password }) => {


With the createComponent method, we will have access to the component instance(componentInstance) and the generated HTML fragment (nativeElement). We test the same thing as before: what is emitted on the output at form submission. The big difference is that we fill the inputs and click the submit button. Filling the inputs with valid data is necessary because the form validation leaves the submit button disabled as long as the inputs contain invalid data.

To make the component work, we have to call the detectChanges method after every change. The detectChanges method does the synchronization of the component instance and the generated HTML. Otherwise the component won't know that the input's value has changed. When we modify an input, triggering the input event manually is also necessary because this is the event that Angular listens for.

Also before doing anything inside the nativeElement, we have to call detectChanges first. Calling the method does the first round of property checks on the component and fills out the template based on it.

Finally, we can use the native DOM methods and selectors on the nativeElement property.

With shallow tests, we gain the ability to test the templates, but it comes with a price. These tests run much slower by including the compilation step.

Integration tests

The next step is to test the component through its interactions with other components. With integration tests, not only the template, but inputs and outputs will also be tested.

The setup is very similar to shallow tests. We have to set up and compile components. The difference is that we have one more component that uses the login form component inside its template. The wrapper component passes down the predefined email address and listens for the submit event.

describe('Integration', () => {
    selector: 'site',
    template: `<login-form [email]="email" (submitted)="onFormSubmit($event)"></login-form>`
  class SiteComponent {
    email = expectedEmail;

    storedEmail: string;

    storedPassword: string;

    onFormSubmit({ email, password }) {
      this.storedEmail = email;
      this.storedPassword = password;

  beforeEach(async(() => {
      declarations: [LoginFormComponent, SiteComponent],
      imports: [FormsModule, ReactiveFormsModule]

  it('should send credentials on submit', () => {
    let fixture = TestBed.createComponent(SiteComponent);
    let component: SiteComponent = fixture.componentInstance;
    let element = fixture.nativeElement;



    element.querySelector('#login-password').value = expectedPassword;
    element.querySelector('#login-password').dispatchEvent(new Event('input'));




The modification of the input fields is the same, but the assertions are different. This time we don't write assertions for the login form, but write them for the wrapper component instead. This way, we ensure the bindings are correct.

There is no considerable slowdown compared to shallow tests. Integration tests need a bit more setup upfront, but we can test the interactions between the components.

Aside: Authenticate an Angular App and Node API with Auth0

We can protect our applications and APIs so that only authenticated users can access them. Let's explore how to do this with an Angular application and a Node API using Auth0. You can clone this sample app and API from the angular-auth0-aside repo on GitHub.

Auth0 login screen


The sample Angular application and API has the following features:

  • Angular application generated with Angular CLI and served at http://localhost:4200
  • Authentication with auth0.js using a login page
  • Node server protected API route http://localhost:3001/api/dragons returns JSON data for authenticated GET requests
  • Angular app fetches data from API once user is authenticated with Auth0
  • Profile page requires authentication for access using route guards
  • Authentication service uses a subject to propagate authentication status events to the entire app
  • User profile is fetched on authentication and stored in authentication service
  • Access token, profile, and token expiration are stored in local storage and removed upon logout

Sign Up for Auth0

You'll need an Auth0 account to manage authentication. You can sign up for a free account here. Next, set up an Auth0 application and API so Auth0 can interface with an Angular app and Node API.

Set Up an Auth0 Application

  1. Go to your Auth0 Dashboard and click the "create a new application" button.
  2. Name your new app and select "Single Page Web Applications".
  3. In the Settings for your new Auth0 app, add http://localhost:4200/callback to the Allowed Callback URLs.
  4. Add http://localhost:4200 to the Allowed Logout URLs. Click the "Save Changes" button.
  5. If you'd like, you can set up some social connections. You can then enable them for your app in the Application options under the Connections tab. The example shown in the screenshot above utilizes username/password database, Facebook, Google, and Twitter. For production, make sure you set up your own social keys and do not leave social connections set to use Auth0 dev keys.

Note: Under the OAuth tab of Advanced Settings (at the bottom of the Settings section) you should see that the JsonWebToken Signature Algorithm is set to RS256. This is the default for new applications. If it is set to HS256, please change it to RS256. You can read more about RS256 vs. HS256 JWT signing algorithms here.

Set Up an API

  1. Go to APIs in your Auth0 dashboard and click on the "Create API" button. Enter a name for the API. Set the Identifier to your API endpoint URL. In this example, this is http://localhost:3001/api/. The Signing Algorithm should be RS256.
  2. You can consult the Node.js example under the Quick Start tab in your new API's settings. We'll implement our Node API in this fashion, using Express, express-jwt, and jwks-rsa.

We're now ready to implement Auth0 authentication on both our Angular client and Node backend API.

Dependencies and Setup

The Angular app utilizes the Angular CLI. Make sure you have the CLI installed globally:

$ npm install -g @angular/cli

Once you've cloned the project, install the Node dependencies for both the Angular app and the Node server by running the following commands in the root of your project folder:

$ npm install
$ cd server
$ npm install

The Node API is located in the /server folder at the root of our sample application.

Find the config.js.example file and remove the .example extension from the filename. Then open the file:

// server/config.js (formerly config.js.example)
module.exports = {
  AUTH0_AUDIENCE: 'http://localhost:3001/api/'

Change the AUTH0_CLIENT_DOMAIN identifier to your Auth0 application domain and set the AUTH0_AUDIENCE to your audience (in this example, this is http://localhost:3001/api/). The /api/dragons route will be protected with express-jwt and jwks-rsa.

Note: To learn more about RS256 and JSON Web Key Set, read Navigating RS256 and JWKS.

Our API is now protected, so let's make sure that our Angular application can also interface with Auth0. To do this, we'll activate the src/app/auth/auth0-variables.ts.example file by deleting .example from the file extension. Then open the file and change the [AUTH0_CLIENT_ID] and [AUTH0_CLIENT_DOMAIN] strings to your Auth0 information:

// src/app/auth/auth0-variables.ts (formerly auth0-variables.ts.example)
export const AUTH_CONFIG: AuthConfig = {

Our app and API are now set up. They can be served by running ng serve from the root folder and node server.js from the /server folder.

With the Node API and Angular app running, let's take a look at how authentication is implemented.

Authentication Service

Authentication logic on the front end is handled with an AuthService authentication service: src/app/auth/auth.service.ts file.

// src/app/auth/auth.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import * as auth0 from 'auth0-js';
import { AUTH_CONFIG } from './auth0-variables';
import { UserProfile } from './profile.model';

export class AuthService {
  // Create Auth0 web auth instance
  // @TODO: Update AUTH_CONFIG and remove .example extension in
  // src/app/auth/auth0-variables.ts.example
  private _Auth0 = new auth0.WebAuth({
    responseType: 'id_token token',
    redirectUri: AUTH_CONFIG.REDIRECT,
  userProfile: UserProfile;
  accessToken: string;
  expiresAt: number;

  // Create a stream of logged in status to communicate throughout app
  loggedIn: boolean;
  loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);

  // NOTE: You can restore an unexpired authentication session on init
  // by using the checkSession() endpoint from auth0.js:

  constructor() {}

  private _setLoggedIn(value: boolean) {
    // Update login status subject
    this.loggedIn = value;

  login() {
    // Auth0 authorize request

  handleLoginCallback() {
    // When Auth0 hash parsed, get profile
    this._Auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken) {
        window.location.hash = '';
      } else if (err) {
        console.error(`Error: ${err.error}`);

  private _setSession(authResult) {
    // Save session data and update login status subject
    this.expiresAt = authResult.expiresIn * 1000 +;
    this.accessToken = authResult.accessToken;
    this.userProfile = authResult.idTokenPayload;

  logout() {
    // Remove token and profile, update login status subject,
    // and log out of Auth0 authentication session
    // This does a refresh and redirects back to homepage
    // Make sure you have the returnTo URL in your Auth0
    // Dashboard Application settings in Allowed Logout URLs
      returnTo: 'http://localhost:4200',

  get authenticated(): boolean {
    // Check if current date is greater than
    // expiration and user is currently logged in
    return ( < this.expiresAt) && this.loggedIn;


This service uses the config variables from auth0-variables.ts to instantiate an auth0.js WebAuth instance.

An RxJS BehaviorSubject is used to provide a stream of authentication status events that you can subscribe to anywhere in the app.

The login() method authorizes the authentication request with Auth0 using your config variables. A login page will be shown to the user and they can then log in.

Note: If it's the user's first visit to our app and our callback is on localhost, they'll also be presented with a consent screen where they can grant access to our API. A first party client on a non-localhost domain would be highly trusted, so the consent dialog would not be presented in this case. You can modify this by editing your Auth0 Dashboard API Settings. Look for the "Allow Skipping User Consent" toggle.

We'll receive accessToken, expiresIn, and idTokenPayload in the hash from Auth0 when returning to our app. The handleLoginCallback() method uses Auth0's parseHash() method callback to set the session (_setSession()) by saving the token, profile, and token expiration and updating the loggedIn$ subject so that any subscribed components in the app are informed that the user is now authenticated.

Note: The profile takes the shape of profile.model.ts from the OpenID standard claims.

Finally, we have a logout() method that logs out of the authentication session on Auth0's server and then redirects back to our app's homepage.

We also have an authenticated accessor to return current authentication status based on presence of a token and the token's expiration.

Once AuthService is provided in app.module.ts, its methods and properties can be used anywhere in our app, such as the home component.

Note: You may need to add (window as any).global = window; to your src/polyfills.ts file if you receive a window error.

Callback Component

The callback component is where the app is redirected after authentication. This component simply shows a loading message until the login process is completed. It executes the handleLoginCallback() method to parse the hash and extract authentication information. It subscribes to the loggedIn$ Behavior Subject from our Authentication service in order to redirect back to the home page once the user is logged in, like so:

// src/app/callback/callback.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { AuthService } from './../auth/auth.service';
import { Router } from '@angular/router';

  selector: 'app-callback',
  templateUrl: './callback.component.html',
  styleUrls: ['./callback.component.css']
export class CallbackComponent implements OnInit, OnDestroy {
  loggedInSub: Subscription;

  constructor(private auth: AuthService, private router: Router) {
    // Parse authentication hash

  ngOnInit() {
    this.loggedInSub = this.auth.loggedIn$.subscribe(
      loggedIn => loggedIn ? this.router.navigate(['/']) : null

  ngOnDestroy() {


Making Authenticated API Requests

In order to make authenticated HTTP requests, we need to add an Authorization header with the access token in our api.service.ts file.

// src/app/api.service.ts
import { Injectable } from '@angular/core';
import { throwError, Observable } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { AuthService } from './auth/auth.service';

export class ApiService {
  private baseUrl = 'http://localhost:3001/api/';

    private http: HttpClient,
    private auth: AuthService
  ) { }

  getDragons$(): Observable<any[]> {
    return this.http
      .get<any[]>(`${this.baseUrl}dragons`, {
        headers: new HttpHeaders().set(
          'Authorization', `Bearer ${this.auth.accessToken}`

  private _handleError(err: HttpErrorResponse | any) {
    const errorMsg = err.message || 'Unable to retrieve data';
    return throwError(errorMsg);


Final Touches: Route Guard and Profile Page

A profile page component can show an authenticated user's profile information. However, we only want this component to be accessible if the user is logged in.

With an authenticated API request and login/logout implemented, the final touch is to protect our profile route from unauthorized access. The auth.guard.ts route guard can check authentication and activate routes conditionally. The guard is implemented on specific routes of our choosing in the app-routing.module.ts file like so:

// src/app/app-routing.module.ts
import { AuthGuard } from './auth/auth.guard';
        path: 'profile',
        component: ProfileComponent,
        canActivate: [

More Resources

That's it! We have an authenticated Node API and Angular application with login, logout, profile information, and protected routes. To learn more, check out the following resources:


We have learnt three methods to test Angular components. The first and fastest is testing the component in isolation: we don't compile the template, just focus on the callable methods, like with a service. If we also want to test the template, we will have to compile that component. These are shallow tests. They are slower, but test more parts. The last missing aspect is the interaction with other components. This can be tested with integration tests. The extra element here is that we need to write a wrapper component around the component under test and observe it through the wrapping component.

Either type we choose, we have to find the right balance between speed and how deeply we test the components. It is optimal if we test business through isolation tests and add some shallow and integration tests to ensure interactions and the template are valid.

To see the tests in action, check out this GitHub repository.