Mocking Angular Applications’ Backend

by Horatiu Dan

Nowadays, most products have their front-ends and back-ends fully decoupled and sometimes developed by different teams. Once the communication interfaces have been agreed upon, the ‘parts’ may be developed either individually, following distinct paths, or together. Irrespective of this aspect, when developing the front-end it is very convenient to be able to focus on its main functionalities and the interaction with the back-end and treat the latter as a black-box.

This post presents a few ways of mocking the data consumed in an Angular application, so that the evolution of the product is smooth and not completely dependent on the back-end services, especially during its development.

Set-up

The front-end application displays a page with Ninjago Series characters, together with a few attributes – name, type, season they first appear in, whether they are favorite or not.

The real data is stored in a database and provided via REST by a back-end service. The assumption is the back-end is not available at this moment, yet the front-end needs to be developed.

The Project

A character is modeled as below (character.ts)

export interface Character {
  id: number;
  type: string;
  name: string;
  season: number;
  favorite: boolean;
}

and rendered in the UI in as a simple component (character.component)

  • character.component.html
<strong>{{ character.name }}</strong>
<div>Type: {{ character.type }}</div>
<div>First seen in season: {{ character.season }}</div>
<div>Favorite: {{ character.favorite }}</div>
<br>
  • character.component.ts
import {Component, Input, OnInit} from '@angular/core';
import {Character} from './character';

@Component({
  selector: 'app-character',
  templateUrl: './character.component.html',
  styleUrls: ['./character.component.css']
})
export class CharacterComponent implements OnInit {

  @Input() character: Character;

  constructor() { }

  ngOnInit(): void {
  }
}

All available characters are displayed in a separate list component (character-list.component).

  • character-list.component.html
<app-character
  *ngFor="let character of characters"
  [character]="character"></app-character>
  • character-list.component.ts
import { Component, OnInit } from '@angular/core';
import {Character} from '../character/character';
import {CharacterService} from '../services/character.service';

@Component({
  selector: 'app-character-list',
  templateUrl: './character-list.component.html',
  styleUrls: ['./character-list.component.css']
})
export class CharacterListComponent implements OnInit {

  characters: Character[];

  constructor(private characterService: CharacterService) { }

  ngOnInit(): void {
    this.initCharacters();
  }

  private initCharacters(): void {
    this.characterService.getCharacters()
      .subscribe(characters => this.characters = characters);
  }
}

As shown above, the data is retrieved via the CharacterService that is injected in the component and stored in the characters array.

In the following sections, 3 ways to simulate the real data flow are presented – the first one is trivial, while the next ones a little bit more interesting and useful.

Mock Data Directly

If we look at the method that populates the characters array in the CharacterListComponentinitCharacters() – we see a call to the getCharacters() service method which is triggered once a subscription is fulfilled, thus the signature of the method is as below.

getCharacters(): Observable<Character[]>

The most straight forward mock implementation of the service is to return the stored array using the rxjs of() function.

import { Injectable } from '@angular/core';
import {Observable, of} from 'rxjs';
import {Character} from '../character/character';

@Injectable({
  providedIn: 'root'
})
export class CharacterService {

  private characters: Character[] = [
    {
      id: 1,
      name: 'Jay',
      type: 'Good',
      season: 1,
      favorite: true
    },
    {
      id: 2,
      name: 'Vanghelis',
      type: 'Evil',
      season: 13,
      favorite: false
    },
    {
      id: 3,
      name: 'Overlord',
      type: 'Evil',
      season: 2,
      favorite: false
    },
    {
      id: 4,
      name: 'Aspheera',
      type: 'Evil',
      season: 11,
      favorite: false
    },
    {
      id: 5,
      name: 'Kay',
      type: 'Good',
      season: 1,
      favorite: true
    }
  ];

  constructor() { }

  getCharacters(): Observable<Character[]> {
    return of(this.characters);
  }
}

If the application is run, the data is serviced and rendered in the UI.

No styling is applied, thus the simplistic appearance.

This is the most straight-forward approach to mock this service, it may be used in a very incipient phase of development, but it is completely unreliable in my opinion. CharacterService is in a temporary state and it will be surely changed when the actual integration with the back-end is done.

The source code for this stage is here – https://github.com/horatiucd/ninjago-front-end/tree/v2.0.0.

Mock using an HttpInterceptor

Angular has a module called HttpClientModule destined for working with HTTP calls from the client. This is an optional module that is included if needed.

As the data between the back-end and the front-end is exchanged via REST, CharacterService uses HttpClient to trigger requests. To make it possible, app.module.ts imports HttpClientModule and declares it as part of the @NgModule imports array.

import {HttpClientModule} from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
    CharacterListComponent,
    CharacterComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

CharacterService is modified to its final version:

import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {Character} from '../character/character';
import {HttpClient} from '@angular/common/http';
import {map} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class CharacterService {

  constructor(private http: HttpClient) { }

  getCharacters(): Observable<Character[]> {
    return this.http.get<Character[]>('/api/v1/characters')
      .pipe(map(data => {
        if (data === null) {
          return [];
        }
        return data.map((character: Character) => character);
      }));
  }
}

getCharacters() method is now in its final form – it performs a HTTP GET to /api/v1/characters endpoint and if available, the data is received. As mentioned before, currently the back-end is not available. Thus, in order for the project to evolve, it needs to be self-contained. Instead of interrogating the real remote server, an HTTP interceptor is used instead, which provides the mocked data.

First the array of characters is moved to /mocks/characters.ts file so that is easily referred to from now on.

export const CHARACTERS: Character[] = [
  {
    id: 1,
    name: 'Jay',
    type: 'Good',
    season: 1,
    favorite: true
  },
  {
    id: 2,
    name: 'Vanghelis',
    type: 'Evil',
    season: 13,
    favorite: false
  },
  {
    id: 3,
    name: 'Overlord',
    type: 'Evil',
    season: 2,
    favorite: false
  },
  {
    id: 4,
    name: 'Aspheera',
    type: 'Evil',
    season: 11,
    favorite: false
  },
  {
    id: 5,
    name: 'Kay',
    type: 'Good',
    season: 1,
    favorite: true
  }
];

Secondly, as part of the same /mocks directory, an HttpInterceptor injectable implementation is created.

import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Character} from '../character/character';
import {CHARACTERS} from './characters';

@Injectable({
  providedIn: 'root'
})
export class CharacterServiceInterceptor implements HttpInterceptor {

  private readonly GET_CHARACTERS_URL = '/api/v1/characters';

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.url === this.GET_CHARACTERS_URL && req.method === 'GET') {
      return this.getCharacters();
    }
    return next.handle(req);
  }

  private getCharacters(): Observable<HttpResponse<Character[]>> {
    return new Observable<HttpResponse<Character[]>>(observer => {
      observer.next(new HttpResponse<Character[]>({
        status: 200,
        body: CHARACTERS
      }));

      observer.complete();
    });
  }
}

In general, an HttpInterceptor allows examining and modifying the HTTP request before passing it back to Angular’s HTTP service. As a common use case, they might be used to add authentication headers to every request, or to alias shorter URL to much longer ones. As already mentioned, in case of this project it is used to mock the HTTP requests towards the back-end service.

Interceptors have only one method

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>

which returns an Observable and takes two arguments. The former is the HttpRequest itself, while the latter is the HttpHandler used to dispatch back the next request in a stream of requests.

The implementation is straight-forward – in case of GET requests towards /api/v1/characters (the same one used in the CharacterService), an observable containing a HTTP 200 status and the CHARACTERS array as body is returned, otherwise it is returned with no changes.

In order to put the interceptor in force, it needs to be wired inside the app.module.ts, in the providers array.

import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {CharacterServiceInterceptor} from './mocks/character-service-interceptor';

@NgModule({
  declarations: [
    AppComponent,
    CharacterListComponent,
    CharacterComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: CharacterServiceInterceptor, multi: true}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

This second mocking approach is way better than the previous one as it allows having the service as close as possible to its final version, while pretending the responses are coming from a real back-end.

The source code for this stage is here – https://github.com/horatiucd/ninjago-front-end/tree/v3.0.0.

Mock using a HttpXhrBackend mock

In order to trigger HTTP calls from Angular, usually a real endpoint is needed – the previously referred back-end. Since Angular has a powerful dependency injection engine and the codebase is constructed upon it, this can be leveraged to replace the HttpXhrBackend class that handles the HTTP calls and set-up a mock for it to simulate these.

In order to accomplish this, the HttpBackend class is extended. According to the documentation, this HttpHandler dispatches the requests via browser HTTP APIs to a backend. Interceptors sit between the HttpClient interface and the HttpBackend. When injected, the HttpBackend dispatches requests directly to the backend, without going through the interceptor chain.

As part of the /mocks directory, MockHttpBackend is added – mock-http-backend.ts

HttpBackend has one abstract method

handle(req: HttpRequest<any>): Observable<HttpEvent<any>>

that returns an Observable and takes the HttpRequest as argument.

The implementation is similar to the one used in the interceptor. In case of GET requests towards /api/v1/characters, an observable containing a HTTP 200 status and the CHARACTERS as body is returned.

import {HttpBackend, HttpEvent, HttpRequest, HttpResponse} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Character} from '../character/character';
import {CHARACTERS} from './characters';

export class MockHttpBackend implements HttpBackend {

  private readonly GET_CHARACTERS_URL = '/api/v1/characters';

  handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
    if (req.url === this.GET_CHARACTERS_URL && req.method === 'GET') {
      return this.getCharacters();
    }
  }

  private getCharacters(): Observable<HttpResponse<Character[]>> {
    return new Observable<HttpResponse<Character[]>>(observer => {
      observer.next(new HttpResponse<Character[]>({
        status: 200,
        body: CHARACTERS
      }));

      observer.complete();
    });
  }
}

This allows building the CharacterService to use the Angular HTTP service and not require a separate API to call. Basically, by leveraging Angular DI architecture, the default HttpXhrBackend class can be replaced with the mock class.

In app.module.ts, both HttpXhrBackend and MockHttpBackend (the mocked type) need to be imported and also registered in the providers array of the @NgModule decorator.

import {HttpClientModule, HttpXhrBackend} from '@angular/common/http';
import {MockHttpBackend} from './mocks/character-service-mock-backend';

@NgModule({
  declarations: [
    AppComponent,
    CharacterListComponent,
    CharacterComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    { provide: HttpXhrBackend, useClass: MockHttpBackend }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Basically, when a HttpXhrBackend is needed, an instance of a MockHttpBackend is used.

The source code for this stage is here – https://github.com/horatiucd/ninjago-front-end/tree/v4.0.0

In my opinion, the 3rd option is the most convenient one, as it allows having the services that use the HttpClient in their final form and under the hood, the calls to the backend are responded from the mocked HttpXhrBackend.

Once a real back-end is available, it is very easy to switch and use it instead.

Leave a comment