AngularJS templates no longer automatically unwrap promises (AngularJS >= 1.2). See https://code.angularjs.org/1.2.24/docs/guide/migration#templates-no-longer-automatically-unwrap-promises
This feature is now deprecated but if you absolutely need it, it can be reenabled for now via the $parseProvider.unwrapPromises(true) API.
In this post we will see how to:
- separate data lookup from controllers logic. It is way way better implement $http calls into dedicated angular services for separation of concerns, good design and software testability
- how to test $http calls with mocks
Meaningful code in bold, feedback please!
Controller
Controller's code
scripts/controllers/car.js:'use strict';
angular.module('angularApp')
.controller('CarCtrl', ['$scope', '$routeParams', 'carFactory', function ($scope, $routeParams, carFactory) {
// route params (example: )
$scope.params = $routeParams;
carFactory.get($scope.params.carId)
.success(function(data) {
$scope.data = data;
});
}]);
Controller's test code
test/spec/controllers/car.js:'use strict';
describe('Controller: CarCtrl', function () {
// load the controller's module
beforeEach(module('angularApp'));
var CarCtrl,
scope,
success;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, $q) {
var promise = $q.when({id: '1', title: 'Audi'});
promise.success = function(fn) {
promise.then(function(response) {
fn(response);
});
return promise;
};
scope = $rootScope.$new();
CarCtrl = $controller('CarCtrl', {
$scope: scope,
$routeParams: {carId: 'audi'},
carFactory: {get: function () {return promise;}}
});
}));
it('should have routeParams into params', function () {
expect(scope.params.carId).toBe('audi');
});
it('should have carFactory data', function () {
scope.$digest();
expect(!!scope.data).toBe(true);
});
});
Service
Service code
The base url of my remote endpoint can be injected with:.value('apiPrefix', 'http://localhost:3001/')if you are using a service and it is a value shared with other components.
Otherwise can use a configurable service (provider).
Using value or a configurable service helps you to mock things injecting different endpoint urls during development.
scripts/services/carfactory.js):
'use strict';
angular.module('angularApp')
.factory('carFactory', ['$http', 'apiPrefix', function ($http, apiPrefix) {
// Service logic
// ...
var base = apiPrefix ? apiPrefix : '';
// Public API here
return {
get: function (carId) {
var promise;
promise = $http.jsonp(base + 'cars/' + carId + '?callback=JSON_CALLBACK');
return promise;
}
};
}]);
Service test code
test/spec/services/carfactory.js:'use strict';
describe('Service: carFactory', function () {
// load the service's module
beforeEach(module('angularApp'));
// instantiate service
var carFactory, $httpBackend, apiPrefix;
beforeEach(inject(function (_carFactory_, _$httpBackend_, _apiPrefix_) {
carFactory = _carFactory_;
$httpBackend = _$httpBackend_;
apiPrefix = _apiPrefix_;
$httpBackend.whenJSONP(apiPrefix + 'cars/audi?callback=JSON_CALLBACK').respond({
id: 'audi',
title: 'Audi'
});
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should do something', function () {
expect(!!carFactory).toBe(true);
});
it('test get data', function () {
var data, promise;
promise = carFactory.get('audi');
promise.success(function(res) {
data = res;
});
$httpBackend.flush();
expect(data.title).toBe('Audi');
});
});
No comments:
Post a Comment
Note: only a member of this blog may post a comment.