Mock your backend with AngularJS and Grunt (Yeoman)
I like to work independent of any backend implementation because of three reasons:
- I don’t want to wait until the backend is implemented if I can not do it by myself
- I like to work offline even in trains when I have no reliable internet connection
- I like the speed of the JS development process without restarting huge backend systems
All together ended in the idea to mock every backend call while developing JS frontends. The first time I was using this approach I was working in a big project where the frontend team had to start the implementation without any ready backend. This project was month before AngularJS was ready to use and we were working with BackboneJS and jQuery. But we got it working even with this setup and we loved the new way to work. So I’m using the same idea in my current AngularJS + Yeoman setup.
Overview
The basic idea is that if you run grunt serve
you get an offline development environment where all backend calls are mocked. But when you build your release grunt
all backend calls are real.
How
I’m bootsrapping a “different” dev-AngularJS app when starting it in development mode then when starting it in production. The difference is that this dev-app uses the production app as module, uses ngMockE2E
and $httpBackend
to mock all backend calls.
Step by step
To see this steps better in a context a demo project is available on github.
1) Add all required packages to your package.json
{
"name": "mockedbackendwithangularjs",
"version": "0.0.0",
"repository": {
"type": "git",
"url": "https://github.com/Pindar/mocked-backend-with-angularjs.git"
},
"dependencies": {},
"devDependencies": {
...
"grunt-processhtml": "^0.3.3"
},
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "grunt test"
}
}
and bower.json
"devDependencies": {
"angular-mocks": "~1.2.0"
},
then call npm install && bower install
2) Configure grunt-processhtml. Add the following to your Gruntfile
processhtml: {
options: {
commentMarker: 'process'
},
dist: {
files: [
{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html', 'views/{,*/}*.html'],
dest: '<%= yeoman.dist %>'
}
]
}
}
grunt.registerTask('build', [
// ...
'processhtml',
'usemin',
'htmlmin'
]);
3) Change way the application gets bootstrapped
a) remove ng-app="mockedBackendWithAngularjsApp"
from the body tag in the index.html
b) add the code to your app.js to bootstrap the regular app
/**
* @ngdoc bootstrap
* @name mockedBackendWithAngularjsApp
*
*/
(function () {
if (!angular.mock) {
angular.element(document).ready(function () {
angular.bootstrap(document, ['mockedBackendWithAngularjsApp']);
});
}
})();
When you call grunt serve afterwards the app should still start.
c) create app-mock.js
angular
.module('mockedBackendWithAngularjsAppDev', ['mockedBackendWithAngularjsApp', 'ngMockE2E'])
.run(function ($httpBackend) {
'use strict';
$httpBackend.whenGET(/^views\//).passThrough();
$httpBackend.whenGET(/^res\//).passThrough();
/* backend API calls here */
$httpBackend.whenPOST(/^\/signup/).respond(200);
$httpBackend.whenGET(/^\/api\/catalog\/US/).respond(200, TD.catalogUS);
$httpBackend.whenPOST(/\/api\/\/exception\/(\S)*/).respond({});
});
if (angular.mock) {
angular.element(document).ready(function () {
'use strict';
angular.bootstrap(document, ['mockedBackendWithAngularjsAppDev']);
});
}
d) add your mock files to ./test/mock, e.g.,
window.TD = window.TD || {};
TD.catalogUS = {
key: 'Hello World!'
};
e) make mock files available – add folder to the livereload task in your Gruntfile
connect().use(
'/mock',
connect.static('./test/mock')
),
f) wire everything together – add files to index.html
<!-- vendor scripts... -->
<!-- process:remove -->
<script src="bower_components/angular-mocks/angular-mocks.js"></script>
<!-- /process -->
<!-- your production scripts -->
<!-- process:remove -->
<script src="mock/catalog-us.js"></script>
<script src="scripts/app-mock.js"></script>
<!-- /process -->
4) Implement your backend calls with $http
as usual. You can find an example in the main.js controller in the demo project.
Enjoy your new offline development environment!