3

I am working on a big angular application which needs to mock entire API in the browser (also called backend-less development). I will have lots of different sections and views in my application, each of them will have its own mock definitions.

I do know that I may use the ngMock module's $httpBackend service which enables me to mock an AJAX call. That's what I want. And I've found a working snippet at jsfiddle.

But the thing that I'm missing is how to split this into multiple files? As I said, my app will have hunderds of pages and possibly even more RESTful resources and I can't simply put this into one source file. This in as architectural question: what is the best approach (that is working, scalable and easy to maintain) to divide thousands of whenGET and whenPOST calls into different files that will simply mock the same API? How to separate mocks in terms of project file structure? Shall there be a separate .run() method for each module in the app? Can I load mocks from JSON files?

I would appreciate an explanation as well as a demo

To make answering easier, below is the relevant part of the fiddle:

myApp.run(function ($httpBackend) {
    var phones = [{name: 'phone1'}, {name: 'phone2'}];

    $httpBackend.whenPOST('/phones').respond(function (method, url, data, headers) {
        console.log('Received these data:', method, url, data, headers);
        phones.push(angular.fromJson(data));
        return [200, {}, {}];
    });

    $httpBackend.whenGET('/phones').respond(function (method, url, data) {
        console.log("Getting phones");
        return [200, phones, {}];
    });

    $httpBackend.whenGET(/\.html$/).passThrough();
});
ducin
  • 25,621
  • 41
  • 157
  • 256
  • 1
    What approach did you follow finally? I am currently in the same situation and want to know the best approach for the same. @ducin – Jagrut Nov 24 '15 at 15:16
  • @Jagrut to answer your question, more or less, what I did was using browserify for bundling. The hunderds-of-calls problem exist in your source code - so you want to split your source into multiple files. But you can use concat/browserify/webpack/(a combination of them) to produce a single target file (that'd be used from `index.html`). So you benefit from having clear & extensible hierarchy in the source *AND* have one big `mock.js` output file in the build (dist, output, whetever you call it). You can also include/exclude the `mock.js` to turn mocks on/off! Hope that helps :) – ducin Nov 25 '15 at 14:26
  • Actually, my team lead wants that I have a separate file containing all $httpBackend calls and that I include that file in all unit tests. Is that possible? Sorry for the late reply. – Jagrut Nov 30 '15 at 15:17
  • @Jagrut Of course it is. You will keep separate file for separate request/response in the *SOURCE*. But in order to test it or to run the app based on mocks, use browserify, grunt, gulp or whatever else to produce one file. I do the exact same thing as well. So you both would be satisfied :) – ducin Nov 30 '15 at 19:15
  • What I did is I created a JSON file containing all the $httpBackend calls and then using karma-json-fixtures-preprocessor was able to include that file in all of my test cases. Rather than the execution part, we are more concerned about maintaining the code and rewriting it. I am not sure if this is a good practice. What do you think? – Jagrut Dec 01 '15 at 14:32
  • @Jagrut I'm not sure the `karma-json-fixtures-preprocessor` is needed. Node.js built-in CommonJS-based `require` function supports loading json files (e.g. `require('./data.json')`), so no need to use that preprocessor I guess. Moreover, I think that keeping all data in one file will lead to great difficulties in maintaining such file. Not to mention you can't combine that with JSON schema or tools like RAML, API Blueprint or swagger. I'd definitely keep mock data in many separate JSON files. – ducin Jan 19 '16 at 21:21
  • Explaination you offered to @Jagrut's first question is great and correct way to do this. however in the source itself how have you done the separation which the question is all about rather than how to generate bundle it at runtime. I am facing same problem where i believe the best way is using run block of every module to add $httpbackend.when* calls and use it for the services/routes in that module only ? is that what you did ? – Shailesh Vaishampayan Apr 12 '17 at 07:28

2 Answers2

0

You can use something like this:

XXXModule.factory('MockedDataProvider', () ->
  getData: (url) ->
    request = new XMLHttpRequest();
    request.open('GET', url, false)
    request.send(null)
    request.response
)
.run([
  '$httpBackend',
  'MockedDataProvider'
  ($httpBackend, MockedDataProvider) ->
    $httpBackend.whenGET(/i18n/).passThrough()
    $httpBackend.whenGET('/api/xxx/yyy').respond(
      (method, url, data) ->
        data = MockedDataProvider.getData('xxx/mocked.json')
        [200, data]
 )
])

You could divide your mocks in separate folders/modules etc... it should work just fine :)

PS. The code is in cofffescript ;)

treestam
  • 11
  • 1
  • Your XMLHttpRequest call is synchronous (see [3rd parameter - async:false](http://en.wikipedia.org/wiki/XMLHttpRequest#The_open_method)). Synchronous AJAX is evil. – ducin Mar 11 '15 at 07:22
0

I've faced a similar question and here is my solution.

Please note, that I'm using generator-cg-angular and file structure was inherited from there.

index.html ..................... main HTML file
app.js ......................... angular module initialization and route setup
httpBackendStub.js ............. backend stub module
/service ....................... angular services folder
    dimension.js ............... example service
    dimension-spec.js .......... example service unit test
    dimension-stub.js .......... example backend stub with routes and data

There are data-concat="false" attributes which means that all backend stubbing will be removed on build.

index.html

<!-- Bower components and angular app scripts included here -->
...
<!-- Backend stubbing -->
<script src="bower_components/angular-mocks/angular-mocks.js" data-concat="false"></script>
<script src="httpBackendStub.js"        data-concat="false"></script>
<script src="service/dimension-stub.js" data-concat="false"></script>
...
<!-- Initial HTML markdown -->

httpBackendStub.js

angular.module('app')
    .config(function($provide) {
        $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
    })
    .run(function ($httpBackend) {
        // pass templates
        $httpBackend.whenGET(/partial\/.*/).passThrough();
    });

service/dimension-stub.js

angular.module('app').run(function ($httpBackend) {

    $httpBackend.whenGET('api/dimensions').respond([
        {
            name: 'Medium',
            ...
        },
        ...
    ]);

});
terales
  • 3,116
  • 23
  • 33
  • 2
    This is not very scalable, because: [1] you are adding each js file into html. In a big project, this could become hundreds or thousands, nt to mention that it's extremely difficult to debug in such number, if someone makes a mistake. Moreover, your data (`dimension-stub`) is hardcoded within js **and** angular structures. It cannt be re-used by anything else (such as grunt or gulp). – ducin May 15 '15 at 08:04
  • Yes, I can agree with your arguments. I've thought about using dynamically loaded JSON, but that was not a case for this particular project. Can your describe your current installation? – terales May 17 '15 at 09:30
  • 2
    I'm storing JSON files that enable me to use them in custom grunt tasks. They are `require`d by browserify and create a bundle along with ngMock files. Overall, one great `mock.js` is built. It is either added as a JS tag into the index.html (in mock mode) or is ignored (and then the app sends requests to the real API). I came up with the conclusion, that angular architeture is very unflexible/greedy and allows only angular-alike components to play well with each other. – ducin May 17 '15 at 14:31