Skip to content

Commit cd28a2e

Browse files
vojtajinaIgorMinar
authored andcommitted
feat(mocks.$httpBackend): add $httpBackend mock
$httpBackend mock allows: - expecting (asserting) requests - stubbing (responding without asserting) Add empty $httpBackend service (currently just wrapper for $browser.xhr)
1 parent 59adadc commit cd28a2e

8 files changed

Lines changed: 944 additions & 444 deletions

File tree

angularFiles.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ angularFiles = {
3333
'src/service/sniffer.js',
3434
'src/service/window.js',
3535
'src/service/http.js',
36+
'src/service/httpBackend.js',
3637
'src/service/locale.js',
3738
'src/directives.js',
3839
'src/markups.js',

src/AngularPublic.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ function ngModule($provide, $injector) {
7878
$provide.service('$filter', $FilterProvider);
7979
$provide.service('$formFactory', $FormFactoryProvider);
8080
$provide.service('$http', $HttpProvider);
81+
$provide.service('$httpBackend', $HttpBackendProvider);
8182
$provide.service('$location', $LocationProvider);
8283
$provide.service('$log', $LogProvider);
8384
$provide.service('$parse', $ParseProvider);

src/angular-mocks.js

Lines changed: 165 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ angular.module.ngMock = function($provide){
2121
$provide.service('$browser', angular.module.ngMock.$BrowserProvider);
2222
$provide.service('$exceptionHandler', angular.module.ngMock.$ExceptionHandlerProvider);
2323
$provide.service('$log', angular.module.ngMock.$LogProvider);
24+
$provide.service('$httpBackend', angular.module.ngMock.$HttpBackendProvider);
2425
};
2526
angular.module.ngMock.$inject = ['$provide'];
2627

@@ -38,8 +39,6 @@ angular.module.ngMock.$inject = ['$provide'];
3839
*
3940
* The following apis can be used in tests:
4041
*
41-
* - {@link #xhr} — enables testing of code that uses
42-
* the {@link angular.module.ng.$xhr $xhr service} to make XmlHttpRequests.
4342
* - $browser.defer — enables testing of code that uses
4443
* {@link angular.module.ng.$defer $defer} for executing functions via the `setTimeout` api.
4544
*/
@@ -720,6 +719,170 @@ angular.module.ngMock.dump = function(object){
720719
}
721720
};
722721

722+
/**
723+
* @ngdoc object
724+
* @name angular.module.ngMock.$httpBackend
725+
*/
726+
angular.module.ngMock.$HttpBackendProvider = function() {
727+
this.$get = function() {
728+
var definitions = [],
729+
expectations = [],
730+
responses = [];
731+
732+
function createResponse(status, data, headers) {
733+
return angular.isNumber(status) ? [status, data, headers] : [200, status, data];
734+
}
735+
736+
// TODO(vojta): change params to: method, url, data, headers, callback
737+
function $httpBackend(method, url, data, callback, headers) {
738+
var xhr = new MockXhr(),
739+
expectation = expectations[0],
740+
wasExpected = false;
741+
742+
if (expectation && expectation.match(method, url)) {
743+
if (!expectation.matchData(data))
744+
throw Error('Expected ' + method + ' ' + url + ' with different data');
745+
746+
if (!expectation.matchHeaders(headers))
747+
throw Error('Expected ' + method + ' ' + url + ' with different headers');
748+
749+
expectations.shift();
750+
751+
if (expectation.response) {
752+
responses.push(function() {
753+
xhr.$$headers = expectation.response[2];
754+
callback(expectation.response[0], expectation.response[1]);
755+
});
756+
return method == 'JSONP' ? undefined : xhr;
757+
}
758+
wasExpected = true;
759+
}
760+
761+
var i = -1, definition;
762+
while ((definition = definitions[++i])) {
763+
if (definition.match(method, url, data, headers || {})) {
764+
if (!definition.response) throw Error('No response defined !');
765+
responses.push(function() {
766+
var response = angular.isFunction(definition.response) ?
767+
definition.response(method, url, data, headers) : definition.response;
768+
xhr.$$headers = response[2];
769+
callback(response[0], response[1]);
770+
});
771+
return method == 'JSONP' ? undefined : xhr;
772+
}
773+
}
774+
throw wasExpected ? Error('No response defined !') :
775+
Error('Unexpected request: ' + method + ' ' + url);
776+
}
777+
778+
$httpBackend.when = function(method, url, data, headers) {
779+
var definition = new MockHttpExpectation(method, url, data, headers);
780+
definitions.push(definition);
781+
return {
782+
then: function(status, data, headers) {
783+
definition.response = angular.isFunction(status) ? status : createResponse(status, data, headers);
784+
}
785+
};
786+
};
787+
788+
$httpBackend.expect = function(method, url, data, headers) {
789+
var expectation = new MockHttpExpectation(method, url, data, headers);
790+
expectations.push(expectation);
791+
return {
792+
respond: function(status, data, headers) {
793+
expectation.response = createResponse(status, data, headers);
794+
}
795+
};
796+
};
797+
798+
$httpBackend.flush = function(count) {
799+
count = count || responses.length;
800+
while (count--) {
801+
if (!responses.length) throw Error('No more pending requests');
802+
responses.shift()();
803+
}
804+
};
805+
806+
807+
808+
$httpBackend.verifyExpectations = function() {
809+
if (expectations.length) {
810+
throw Error('Unsatisfied requests: ' + expectations.join(', '));
811+
}
812+
};
813+
814+
$httpBackend.resetExpectations = function() {
815+
expectations = [];
816+
responses = [];
817+
};
818+
819+
return $httpBackend;
820+
};
821+
};
822+
823+
function MockHttpExpectation(method, url, data, headers) {
824+
825+
this.match = function(m, u, d, h) {
826+
if (method != m) return false;
827+
if (!this.matchUrl(u)) return false;
828+
if (angular.isDefined(d) && !this.matchData(d)) return false;
829+
if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
830+
return true;
831+
};
832+
833+
this.matchUrl = function(u) {
834+
if (!url) return true;
835+
if (angular.isFunction(url.test)) {
836+
if (!url.test(u)) return false;
837+
} else if (url != u) return false;
838+
839+
return true;
840+
};
841+
842+
this.matchHeaders = function(h) {
843+
if (angular.isUndefined(headers)) return true;
844+
if (angular.isFunction(headers)) {
845+
if (!headers(h)) return false;
846+
} else if (!angular.equals(headers, h)) return false;
847+
848+
return true;
849+
};
850+
851+
this.matchData = function(d) {
852+
if (angular.isUndefined(data)) return true;
853+
if (data && angular.isFunction(data.test)) {
854+
if (!data.test(d)) return false;
855+
} else if (data != d) return false;
856+
857+
return true;
858+
};
859+
860+
this.toString = function() {
861+
return method + ' ' + url;
862+
};
863+
}
864+
865+
function MockXhr() {
866+
867+
// hack for testing $http
868+
MockXhr.$$lastInstance = this;
869+
870+
this.getResponseHeader = function(name) {
871+
return this.$$headers[name];
872+
};
873+
874+
this.getAllResponseHeaders = function() {
875+
var lines = [];
876+
877+
angular.forEach(this.$$headers, function(value, key) {
878+
lines.push(key + ': ' + value);
879+
});
880+
return lines.join('\n');
881+
};
882+
883+
this.abort = noop;
884+
}
885+
723886
window.jstestdriver && (function(window){
724887
/**
725888
* Global method to output any number of objects into JSTD console. Useful for debugging.

src/service/http.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ function transform(data, fns, param) {
5151
/**
5252
* @ngdoc object
5353
* @name angular.module.ng.$http
54+
* @requires $httpBacked
5455
* @requires $browser
5556
* @requires $exceptionHandler
5657
* @requires $cacheFactory
@@ -85,8 +86,8 @@ function $HttpProvider() {
8586
}
8687
};
8788

88-
this.$get = ['$browser', '$exceptionHandler', '$cacheFactory', '$rootScope',
89-
function($browser, $exceptionHandler, $cacheFactory, $rootScope) {
89+
this.$get = ['$httpBackend', '$browser', '$exceptionHandler', '$cacheFactory', '$rootScope',
90+
function($httpBackend, $browser, $exceptionHandler, $cacheFactory, $rootScope) {
9091

9192
var cache = $cacheFactory('$http'),
9293
pendingRequestsCount = 0;
@@ -235,15 +236,15 @@ function $HttpProvider() {
235236
/**
236237
* Represents Request object, returned by $http()
237238
*
238-
* !!! ACCESS CLOSURE VARS: $browser, $config, $log, $rootScope, cache, pendingRequestsCount
239+
* !!! ACCESS CLOSURE VARS: $httpBackend, $browser, $config, $log, $rootScope, cache, pendingRequestsCount
239240
*/
240241
function XhrFuture() {
241242
var rawRequest, cfg = {}, callbacks = [],
242243
defHeaders = $config.headers,
243244
parsedHeaders;
244245

245246
/**
246-
* Callback registered to $browser.xhr:
247+
* Callback registered to $httpBackend():
247248
* - caches the response if desired
248249
* - calls fireCallbacks()
249250
* - clears the reference to raw request object
@@ -265,7 +266,7 @@ function $HttpProvider() {
265266
* Fire all registered callbacks for given status code
266267
*
267268
* This method when:
268-
* - serving response from real request ($browser.xhr callback)
269+
* - serving response from real request
269270
* - serving response from cache
270271
*
271272
* It does:
@@ -368,7 +369,7 @@ function $HttpProvider() {
368369
fireCallbacks(fromCache[1], fromCache[0]);
369370
});
370371
} else {
371-
rawRequest = $browser.xhr(cfg.method, cfg.url, data, done, headers, cfg.timeout);
372+
rawRequest = $httpBackend(cfg.method, cfg.url, data, done, headers, cfg.timeout);
372373
}
373374

374375
pendingRequestsCount++;

src/service/httpBackend.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
function $HttpBackendProvider() {
2+
this.$get = ['$browser', function($browser) {
3+
return $browser.xhr;
4+
}];
5+
}
6+

0 commit comments

Comments
 (0)