One important aspect of building a frontend is testing your application against various kind of data that usually comes from some backend API.
One way to make this easier is the ability to mock the data so you can work independently with the frontend part without the need of a real API being ready and easier to test cases not easily reproducable with a real backend.
So let’s get right into it, I’ll assume that you already have an Angular project otherwise you can create a project with command ‘ng new my-mock-app’.
I have an example project here that you can check the end result of: https://github.com/nerdic-coder/my-mock-app
The API that I will create a mock for in this example is: https://api.punkapi.com/v2/beers
Setting up the environment
First thing you need to do is adding a new Angular environment, new Angular projects comes with two environments ‘default’ (actually don’t have a name but usually used for test environment) and ‘prod’ for production builds.
So we will add a ‘mock’ configuration, in your project folder under ‘/src/environments/’ create a new file ‘environment.mock.ts’ and copy the content from ‘environment.prod.ts’ in my example in the prod file the API endpoint is ‘https://api.punkapi.com/v2/’ but in the mock it should point to ‘http://localhost:4200/mock/’, localhost:4200 is the default place an Angular app is served on, if you serve it in any other way change the url and port to that.
Set property ‘production’ to false.
You also need to add a new property called ‘mock’ with the value true, you also need to add it to the other environment files but with the value false.
Next thing you need to add is some configuration to the ‘angular.json’ file, under “build” -> “configurations” you should already have a block for “production”, create a new block under it called “mock”, it should look something like this:
"mock": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.mock.ts"
}
],
"assets": [
{
"glob": "**/*",
"input": "src/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "src/mock",
"output": "mock"
}
],
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": false,
"extractLicenses": false,
"vendorChunk": false,
"buildOptimizer": false
}
}
The “assets” here have to be identical to the default “assets” plus the copy of the mock folder. Sadly the “assets” from default is not inherited by configurations if the configuration have it’s own “assets” block.
Next thing you need to add is a “mock” block under “serve” -> “configurations”:
"mock": {
"browserTarget": "my-mock-app:build:mock"
}
and under “e2e”-> “configurations” if you plan to use mock data for auto tests:
"mock": {
"devServerTarget": "my-mock-app:serve:mock"
}
Now to start in mock mode you can run ‘ng serve –configuration mock’.
I would also recommend to att a script to package json like this:
"mock": "ng serve --configuration mock"
Then you can start it with command ‘npm run mock’.
Adding mock data
Now of course you will get a 404 responses since there is no mock data available yet.
Start with creating a ‘mock’ folder in your projects folder ‘/src/’, under there you create files in the relative paths to where the app tries to call the api on.
So in my example app we create a ‘beers’ file in the mock folder, if the beers api would have been on a deeper path then I would have created a folder for each subpath.
Now I will add a list of beers to my mock file:
[
{
"id": 1,
"name": "Buzz",
"tagline": "A Real Bitter Experience."
},
{
"id": 2,
"name": "Trashy Blonde",
"tagline": "You Know You Shouldn't"
},
{
"id": 3,
"name": "Berliner Weisse With Yuzu - B-Sides",
"tagline": "Japanese Citrus Berliner Weisse."
},
{
"id": 4,
"name": "Pilsen Lager",
"tagline": "Unleash the Yeast Series."
},
{
"id": 5,
"name": "Avery Brown Dredge",
"tagline": "Bloggers' Imperial Pilsner."
}
]
Now the app will show a list from this mock file. This way you can play around with different possible responses from the API to test various scenarios to verify that your app is reacting they way you intended.
Another benefit is that you can continue working with the app even if the backend servers is down for some reason.
How about POST data?
With this solution POST requests will not be working with this solution out of the box, to emulate a POST request I created a mock http interceptor for the app.
Create a file ‘/src/app/http-interceptors/mock-interceptor.ts’ with the following content:
import { Injectable } from '@angular/core';
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
/** Pass untouched request through to the next request handler. */
@Injectable()
export class MockInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!environment.mock) {
return next.handle(req);
} else {
const mockReq = req.clone({
method: 'GET',
});
return next.handle(mockReq);
}
}
}
So what it does is if the mode is not mock it just passes the request forward to the next handler, if the app is in mock mode it converts the request to a GET request from whatever request originally intended and that makes it possible to also add mock responses for POST and other request methods.
You also need to add the interceptor to the ‘app.module.ts’ file under “providers”:
{ provide: HTTP_INTERCEPTORS, useClass: MockInterceptor, multi: true }
Conclusion
That’s all for this simple mock solution, it’s not perfect but a good option if you don’t want to go for a more advanced mock solution. Hope you enjoyed and until next time stay nerdic!
The commit related to the mock changes: https://github.com/nerdic-coder/my-mock-app/commit/2401f99a95e70a359e508340698c2aeeeeafd2de?branch=2401f99a95e70a359e508340698c2aeeeeafd2de&diff=unified
Leave a Reply