Menu

In-Memory Web API for Testing Angular Apps

May 22, 2017 by Christopher Sherman

Front end and middleware components must agree on an interface for authentication, authorization, exchanging data, etc., and engineers developing these components often work on the same team or closely together. These components, however, do not require tight coupling, and separately developing and running components can increase feature velocity and improve reliability. To separate concerns between an Angular application and its middleware, let’s set up an in-memory API that runs in the debugging environment, while automatically switching to the HTTP API when the application runs in production.

Configuration

To get started, create an Angular CLI project.

Open a terminal to the project directory, and run the command npm install --save-dev angular-in-memory-web-api. This project gives us the bulk of the functionality we need to run an in-memory API.

Next, create a directory named in-memory-api (my-app/src/app/in-memory-api). Open a terminal to this directory, and run the command ng g service app-in-memory-api. This service will act as our data repository, returning the data to API requests.

import { InMemoryDbService } from 'angular-in-memory-web-api';
export class AppInMemoryApi implements InMemoryDbService {
createDb() {
let heroes = [
{
name: 'Superman',
description: 'Flies through the air to save damsels in distress.',
powers: ['flight']
},
{
name: 'Batman',
description: 'Responds stealthily to rescue citizens of Gotham from crime.',
powers: ['technology']
}
]
return { 'heroes': heroes };
}
}

The return object key is the name of the API route (which becomes /api/heroes by default), and the value is the data we want to return when the in-memory API receives a request to this route.

With the data store created, create a directory named modules (my-app/src/app/modules). Open the terminal to this directory, and run the command ng g module app-http. This module will return Angular’s HTTP service when the application is running in production, and our in-memory API service otherwise.

import { NgModule, Injector } from '@angular/core';
import {
HttpModule, XHRBackend, BrowserXhr,
ResponseOptions, XSRFStrategy
} from '@angular/http';
import {
InMemoryBackendService,
InMemoryDbService
} from 'angular-in-memory-web-api';
import { AppInMemoryApi } from '../in-memory-api/app-in-memory-api.service'
import { environment } from '../../environments/environment';
export function getXHRBackend(injector: Injector, browser: BrowserXhr,
xsrf: XSRFStrategy, options: ResponseOptions): any {
if (environment.production) {
return new XHRBackend(browser, options, xsrf);
} else {
return new InMemoryBackendService(
injector,
new AppInMemoryApi(),
{apiBase: 'api/latest/'}
);
}
}
@NgModule({
imports: [HttpModule],
providers: [
{
provide: XHRBackend,
useFactory: getXHRBackend,
deps: [Injector, BrowserXhr, XSRFStrategy, ResponseOptions]
}
]
})
export class AppHttpModule {
}

The InMemoryDbService has a number of options described on the Github page for customizing the service to fit the needs of specific APIs. Inside the getXHRBackend function is where we can configure these options. In the example above, I’ve changed the base route of the in-memory API from api to api/latest to demonstrate how we can adjust the default route for situations like API versioning.

Finishing Touches

Finally, open app.module.ts to import the custom HTTP service we created. Add an import statement to the top of the file and the AppHttpModule reference within the imports section of @NgModule.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppHttpModule } from './modules/app-http.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
FormsModule,
AppHttpModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Now, run ng serve and point the browser to http://localhost:4200/api/latest/heroes. The data we added to our in-memory data store will be returned to the browser.

Conclusion

From here, we can add additional data and endpoints to the AppInMemoryApi class and call these from components throughout the application using the Angular Http module. With this framework in place, we can effectively engineer the front end of an application in isolation until we’re ready for integration testing with a middleware component. To run the application against the HTTP API rather than the in-memory API, run ng serve --prod.

Angular