How to mock get(id) requests

2019-05-07 19:20发布

问题:

I am building an application prototype and try to mock the REST web-services.

Here is my code:

var mock = angular.module('mock', ['ngMockE2E']);
mock.run(function($httpBackend){
    users = [{id:1,name:'John'},{id:2,name:'Jack'}];
    $httpBackend.whenGET('/users').respond(users);
    $httpBackend.whenGET(new RegExp('\\/users\\/[0-9]+')).respond(users[0]);
}

Everything is ok, my resource User.query() returns all users, and User.get({id:1}) and User.get({id:2}) returns the same user (John).

Now to improve my prototype, I would like to return the appropriate user, matching the good id.

I read in the angular documentation I should be able to replace the RegExp URI by a function. The idea is to extract the id from the url to use it in respond method. I then tried this:

$httpBackend.whenGET(new function(url){
    alert(url);
    var regexp = new RegExp('\\/users\\/([0-9]+)'); 
    id = url.match(regexp)[1];  
    return regexp.test(url);
}).respond(users[id]);

The problem is the url parameter is always undefined. Any idea to achieve my goal?

回答1:

By using new function(url) your app tries to instantiate a new object from your anonymous function and pass that new object as the first argument of the $httpBackend.whenGET() call.
Of course, at the time of calling whenGET() no URL is provided, thus it is always undefined.

You should pass the function itself (and not an object instanciated using the function). E.g.:

$httpBackend.whenGET(function (url) {
  ...
}).respond(users[id]);

UPDATE:
After some more digging it turned out that the option to pass a function as the first argument to whenGET was added in version 1.3.0-beta.3. The docs you were reading probably referred to the latest beta version, while you were using an earlier version.
(Note that even versions 1.3.0-beta.1 and 2 did not provide this option.)

Without getting into much detail, responsible for verifying a matching URL is MockHttpExpectation's matchUrl method:

function MockHttpExpectation(method, url, data, headers) {
  ...
  this.matchUrl = function(u) {
    if (!url) return true;
    if (angular.isFunction(url.test)) return url.test(u);
    if (angular.isFunction(url)) return url(u);   // <<<<< this line does the trick
    return url == u;
  };

The line if (angular.isFunction(url)) return url(u); is the one that gives the option to directly pass a function and was added in version 1.3.0-beta.3 (as already mentioned). But, if you still want to pass a function to a previous AngularJS version, you could "trick" angular into believing you passed a RegExp, by providing an object with a test method.
I.e. replace:

.whenGET(function (url) {...})

with:

.whenGET({test: function (url) {...}})

See, also, this short demo.



回答2:

I found a solution by using a function in the respond part instead of the when part:

$httpBackend.whenGET(new RegExp('\\/users\\/[0-9]+')).respond(
    function(method, url){
        var regexp = new RegExp('\\/users\\/([0-9]+)');
        var mockId = url.match(regexp)[1];
        return [200, users[mockId]];
    }
});