Setting Up Angular 2 MockBackend

Todd Leininger Angular, Development Technologies, JavaScript, Tutorial 10 Comments

Attention: The following article was published over 7 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

I recently finished up on a client project developing one of the company’s first Angular 2 applications, which would be the first such application to go to production. I was on a team that consisted of only two developers: one for the backend RESTful web services and myself on the frontend Angular 2 application.

When we first were getting the project off the ground, I found that I needed access to the REST services before they were ready. I needed a good way to mock them up. In this situation, I set up a mock service for an Angular 2 application using MockBackend.

In this article I show a step-by-step example of setting up a mock of RESTful web service APIs for an Angular 2 application using Angular’s MockBackend.

Setup

Before we get started, let’s make sure that we have everything we need to work with our example application.

The tutorial uses Git as its versioning system. You can check for what version of Git you have installed by running. You can download and install Git from http://git-scm.com/download.

$ git --version

We are also using Node.js. You can download a Node.js installer from https://nodejs.org/en/download/. You can check your version of Node.js to make sure everything installed smoothly by running the following command:

$ node --version 

Now that we have those two pieces, let’s clone the angular-mockbackend repository located at GitHub by using the following command:

$ git clone https://github.com/TLein77/angular-mockbackend.git

Change your current directory to the angular-mockbackend:

$ cd angular-mockbackend

The last part of the setup will be to install all the necessary Node Modules with the following command:

$ npm install

Once the install is complete everything should be set up to run the Angular application from where we are going to start. Run the following command to start the application:

$ npm start

Once the application is started, go to http://localhost:8181 and you should see the start of Angular’s Tour of Heroes tutorial application. I have used this as a starting point for this tutorial and more information can be found at https://angular.io/docs/ts/latest/tutorial/.

Angular in-memory-web-api

The code is currently set up to use Angular in-memory-web-api for mocking all the data and API calls. This is one way to mock our calls, but exists to support the Angular documentation. Because we are going to implement MockBackend, let’s go ahead and pull out the code that is needed for the in-memory-web-api.

First, let’s remove the dependency from our package.json. The new dependency block will be:

json
"dependencies": {
    "@angular/common": "~2.3.0",
    "@angular/compiler": "~2.3.0",
    "@angular/core": "~2.3.0",
    "@angular/forms": "~2.3.0",
    "@angular/http": "~2.3.0",
    "@angular/platform-browser": "~2.3.0",
    "@angular/platform-browser-dynamic": "~2.3.0",
    "@angular/router": "~3.3.0",
    "bootstrap": "3.3.7",
    "core-js": "^2.4.1",
    "font-awesome": "4.6.3",
    "jquery": "^3.1.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "5.0.0-rc.4",
    "zone.js": "^0.7.2"
  },

Next remove the in-memory-data-service.ts file from src/app.

In src/app/app.module.ts, we need to remove the import of the InMemoryWebApiModule and InMemoryDataService. We also need to remove InMemoryWebApiModule.forRoot(InMemoryDataService from the Module imports.

When done, the app.module.ts should look like the following:

javascript
import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {AppComponent} from "./app.component";
import {FormsModule} from "@angular/forms";
import {HeroDetailComponent} from "./hero-detail/hero-detail.component";
import {HeroesComponent} from "./heroes/heroes.component";
import {HeroService} from "./hero/hero.service";
import {DashboardComponent} from "./dashboard/dashboard.component";
import {AppRoutingModule} from "./app-routing.module";
import {HttpModule} from "@angular/http";

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        AppRoutingModule,
        HttpModule
    ],
    declarations: [
        AppComponent,
        HeroesComponent,
        HeroDetailComponent,
        DashboardComponent,
    ],
    providers: [HeroService],
    bootstrap: [AppComponent]
})
export class AppModule {}

The last bit that needs changed is the URL for the API calls. The in-memory-web-api URLs for this app are specified as api/heroes in src/app/hero/hero.service.ts. By using MockBackend we can specify the URL to be what the real URL will be, or at least what we believe it will be. Let’s change the heroesUrl to be the following:

private heroesUrl = 'http://localhost:8080/heroes';

If we start our application again, everything appears to be fine, until we go the application in our browser. Our API URL isn’t pointing to anything that is real at this point. Let’s change that by setting up the MockBackend.

MockBackend Setup

MockBackend is used specifically for testing the Http service. The class is used to override providers to other backends. So let’s start by editing the app.module.ts so our applications will be using the MockBackend instead of the Http service.

In app.module.ts we should have the following:

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        AppRoutingModule,
        HttpModule
    ],
    declarations: [
        AppComponent,
        HeroesComponent,
        HeroDetailComponent,
        DashboardComponent,
    ],
    providers: [HeroService],
    bootstrap: [AppComponent]
})
export class AppModule {}

First let’s comment out the HttpModule in the imports array:

javascript
imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    //HttpModule
],

I am leaving the HttpModule in for the purpose that our app will not go to production with this set up. At some point we will want this to go to the real APIs for the application, and those will use the Http service provided by the HttpModule.

Now we need to bring in MockBackend and BaseRequestOptions to setup our mock Http service. Add MockBackend and BaseRequestOptions to the providers array:

MockBackend,
BaseRequestOptions

The following imports will also need to be added:

import {MockBackend} from "@angular/http/testing";
import {BaseRequestOptions} from "@angular/http";

Next we want to define and set our mock Http service in our providers array. We specify that this is a Provider for Http that depends on MockBackend and BaseRequestOptions.

Add the following to the providers array:

{
    provide: Http,
    deps: [MockBackend, BaseRequestOptions],
    useFactory: (backend: MockBackend, options: BaseRequestOptions) => { return new Http(backend, options); }
}

We also need to add the Http import:

import {BaseRequestOptions, Http} from "@angular/http";

After all of that, the AppModule code will look like the following:

import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {AppComponent} from "./app.component";
import {FormsModule} from "@angular/forms";
import {HeroDetailComponent} from "./hero-detail/hero-detail.component";
import {HeroesComponent} from "./heroes/heroes.component";
import {HeroService} from "./hero/hero.service";
import {DashboardComponent} from "./dashboard/dashboard.component";
import {AppRoutingModule} from "./app-routing.module";
import {MockBackend} from "@angular/http/testing";
import {BaseRequestOptions, Http} from "@angular/http";
//import {HttpModule} from "@angular/http";

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        AppRoutingModule,
        //HttpModule
    ],
    declarations: [
        AppComponent,
        HeroesComponent,
        HeroDetailComponent,
        DashboardComponent,
    ],
    providers: [
        HeroService,
        MockBackend,
        BaseRequestOptions,
        {
            provide: Http,
            deps: [MockBackend, BaseRequestOptions],
            useFactory: (backend: MockBackend, options: BaseRequestOptions) => { return new Http(backend, options); }
        }
    ],
    bootstrap: [AppComponent]
})
export class AppModule {}

Create the MockBackend

The application is now all setup to use MockBackend. All Http service calls will now be intercepted by the MockBackend and processed.

So let’s now tell the MockBackend what to do when it gets the API calls.

First, create a new directory within your app named mock-backend.

	src/app/mock-backend

For my applications I like to keep my mock data in a separate file from the rest of the code. For this example create a mock-heroes.ts.

src/app/mock-backend/mock-heroes.ts

Now all we need is some data. Place the following in mock-heroes.ts:

export const HEROES: any[] = [
    {id: 11, name: 'Mr. Nice'},
    {id: 12, name: 'Narco'},
    {id: 13, name: 'Bombasto'},
    {id: 14, name: 'Celeritas'},
    {id: 15, name: 'Magneta'},
    {id: 16, name: 'RubberMan'},
    {id: 17, name: 'Dynama'},
    {id: 18, name: 'Dr IQ'},
    {id: 19, name: 'Magma'},
    {id: 20, name: 'Tornado'}
];

Now let’s create the backend. Create mock-backend.service.ts.

src/app/mock-backend/mock-backend.service.ts

I have labeled this as a service and will make this Injectable:

@Injectable()
export class MockBackendService {

}

We start by adding a constructor for the class that will inject MockBackend:

constructor(
	private backend: MockBackend
) {}

Now define a function called start. This will be how the mock backend is started for the application.

start(): void {

}

Now we tell the backend what to do. We start by taking the MockBackend we injected and subscribing to its connections. Now we can look at each connection and tell what we want to do with the connection. I also supply the URL that we will be testing for and a regular expression to tell if we are passing a hero’s id.

this.backend.connections.subscribe((c: MockConnection) => {
	const URL = "http://localhost:8080/heroes";
	let heroesIdRegex = /\/heroes\/([0-9]+)/i;
});

If we go back and look at our hero.service.ts in src/app/hero/, we see that there are two methods that are calling our API. One to return a list of all the Heroes and one to get a specific Hero by id.

Let’s start by mocking up the API for the list of Heroes, that way we can see our mocked backend in action.

In mock-backend.service.ts, let’s look at our MockConnection and see if it is the API call for the list of heroes. We will do two things.

if (c.request.url === URL && c.request.method === 0) {

}

We check that the request URL does indeed match the Heroes URL, and we check that the request method is GET. If both of these are true, the we can return the list of Heroes.

To do so we create a new Response with the JSON of the heroes in the body of the response. Then we respond using the MockConnection’s mockRespond.

c.mockRespond(new Response(new ResponseOptions({
	body: JSON.stringify(HEROES)
})));

Our entire start function should now look like the following:

start(): void {
    this.backend.connections.subscribe((c: MockConnection) => {
        const URL = "http://localhost:8080/heroes";
        let heroesIdRegex = /\/heroes\/([0-9]+)/i;

        if (c.request.url === URL && c.request.method === 0) {
            c.mockRespond(new Response(new ResponseOptions({
                body: JSON.stringify(HEROES)
            })));
        }
    });
}

Our entire mock-backend.service.ts to this point will be:

import {Injectable} from "@angular/core";
import {MockBackend, MockConnection} from "@angular/http/testing";
import {HEROES} from "./mock-heroes";
import {ResponseOptions, Response} from "@angular/http";

@Injectable()
export class MockBackendService {
    constructor(
        private backend: MockBackend
    ) {}

    start(): void {
        this.backend.connections.subscribe((c: MockConnection) => {
            const URL = "http://localhost:8080/heroes";
            let heroesIdRegex = /\/heroes\/([0-9]+)/i;

            if (c.request.url === URL && c.request.method === 0) {
                c.mockRespond(new Response(new ResponseOptions({
                    body: JSON.stringify(HEROES)
                })));
            }
        });
    }
}

Use the Mocked Backend

We have our backend mocked up, but we haven’t used it yet. For that we need to inject it into our application.

Open src/app/app.component.ts. Let’s add some code to have our mock backend start on the construction of the AppComponent.

First let’s add the constructor and insert our MockBackendService:

constructor(
private mockBackendService: MockBackendService
) {

}

Now in our constructor we can go ahead and call start on the service:

this.mockBackendService.start();

We also need to tell Angular that the MockBackendService is a provider, so let’s add that to our component. It will now look like this:

@Component({
    selector: 'my-app',
    templateUrl: 'app.component.html',
    styleUrls: ['app.component.css'],
    providers: [MockBackendService]
})

Here is how the AppComponent should now look:

import {Component} from "@angular/core";
import {MockBackendService} from "./mock-backend/mock-backend.service";

import '../../public/css/styles.css';

@Component({
    selector: 'my-app',
    templateUrl: 'app.component.html',
    styleUrls: ['app.component.css'],
    providers: [MockBackendService]
})
export class AppComponent {
    title = 'Tour of Heroes';

    constructor(
        private mockBackendService: MockBackendService
    ) {
        this.mockBackendService.start();
    }
}

The last thing we will need to do is adjust what the HeroService is reading from the response to set as the Hero[]. In the getHeroes() and getHero(id:number) functions, change the following:

response.json().data

to

response.json()

Now when we run $ npm start and open the application at http://localhost:8181, we can see the dashboard displaying five heroes. If we click on a hero we still won’t see their details though. Let’s add that next.

The process will be the same as mocking the getHeroes() function. We are just testing that it matches the heroesIdRegex and that the request method is GET. We then do a little more processing to return the correct Hero.

Below is the completed MockBackendService including the getHero function:

import {Injectable} from "@angular/core";
import {MockBackend, MockConnection} from "@angular/http/testing";
import {HEROES} from "./mock-heroes";
import {ResponseOptions, Response} from "@angular/http";

@Injectable()
export class MockBackendService {
    constructor(
        private backend: MockBackend
    ) {}

    start(): void {
        console.log('MockBackendService start');
        this.backend.connections.subscribe((c: MockConnection) => {
            console.log('mockConnection url:: ' + c.request.url);
            const URL = "http://localhost:8080/heroes";
            let heroesIdRegex = /\/heroes\/([0-9]+)/i;


            if (c.request.url === URL && c.request.method === 0) {
                console.log(JSON.stringify(HEROES));
                c.mockRespond(new Response(new ResponseOptions({
                    body: JSON.stringify(HEROES)
                })));
            } else if (c.request.url.match(heroesIdRegex) && c.request.method === 0) {
                let matches = HEROES.filter((hero) => {
                    return hero.id === +(c.request.url.match(heroesIdRegex)[1])
                });
                c.mockRespond(new Response( new ResponseOptions({
                    body: JSON.stringify(matches[0])
                })));
            }
        });
    }
}

Last Thoughts

Through this entire process we have set up a mock of our RESTful Webservice APIs. We can now use that mock to work on our application before the real APIs are ready. Once we are done with the mockup, we can then dispose of our provider for the Http service and switch back to using the HttpModule. Then we remove the provider from the AppComponent and the constructor and we are back to making the real API calls.

This is just one example of using the MockBackend. We can and should also use this for implementation in our unit tests.

Thanks for spending your time with me, and feel free to let me know of any questions.

0 0 votes
Article Rating
Subscribe
Notify of
guest

10 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments