I am trying to Unit Test an AngularJS HTTP Service with the help of Karma-Jasmine.
But I am facing the following Error from Karma:
Error: Unexpected request: GET app/license/license.html
I Googled and found out that it has something to do with ui-router.
The following StackOverflow Link: (UI-router interfers with $httpbackend unit test, angular js) suggests some Answers, but none of them works for me and the Error remains the same.
I am posting my code below for reference:
licensedata.service.js
(function () {
"use strict";
angular
.module("app")
.factory("licenseDataService", licenseDataService)
licenseDataService.$inject = ["$http"];
function licenseDataService($http) {
return {
getLicenseSpecs: getLicenseSpecs
};
function getLicenseSpecs() {
return $http.get("http://localhost:8080/?command=print-spec")
.then(success)
.catch(fail);
function success(response) {
return response.data;
}
function fail(e) {
return e.data;
}
}
}
})();
licensedata.service.spec.js
describe("LICENSE DATA SERVICE", function () {
var licenseDataService;
var httpBK;
beforeEach(angular.mock.module("app"));
beforeEach(angular.mock.module(function ($urlRouterProvider) {
$urlRouterProvider.deferIntercept();
}));
beforeEach(inject(function (_licenseDataService_, $httpBackend) {
licenseDataService = _licenseDataService_;
httpBK = $httpBackend;
}));
it("Test License Key", function () {
var returnData = {};
httpBK.expectGET("http://localhost:8080/?command=print-spec").respond(returnData);
var returnedPromise = licenseDataService.getLicenseSpecs();
var result;
returnedPromise.then(function (response) {
result = response.data;
});
httpBK.flush();
});
});
Any help will be greatly appreciated.
Related
I have an AngularJs application working with components and several modules. I created a plunker example to present my problem here.
I have my NavbarComponent where I declared my Controller where I inject my service called NavbarService.
In the NavbarService, I inject a factory resource to make my Rest call, once this call is made I'm trying to made some treatment on the response before returning it back to the controller, in this example I just apply a simple filter on it, but it doesn't work. If I omit my treatment and return only the categories, the code works and you can visualize a list of two.
I can make my treatment in the controller but this is a bad practice 'cause I believe it should be done in the Service, secondly since it's an asynchronous response I must do something like this to make it work, which is really really ugly:
navbarService.getCategories().$promise.then(function (response) {
console.log("controller", response[0].category);
vm.categories = categoryFilter(response[0].category);
}, function (error) {
console.log("an error occured");
});
Can someone please guide me through this, I'm out of solutions. Thank you
Another simple way is to pass a callback function to service from you component like this
'use strict';
angular.module('navbar').component('appNavbar', {
templateUrl: "navbar.template.html",
controller: [ 'navbarService', function appNavbarController(navbarService) {
var vm = this;
navbarService.getCategories(function(data){
// this will be called when service will get the response and filter function has filtered the response
vm.categories = data;
});
}]
});
Now service should be like this
'use strict';
angular.module('navbar').service('navbarService', ['categoryResourceService', 'categoryFilter', function(categoryResourceService, categoryFilter) {
var vm = this;
vm.getCategories = function(callback) {
categoryResourceService.query(function(response) {
console.log("[NavbarService] response:", response);
callback(categoryFilter(response));
}, function(error) {
console.log("[NavbarService] error:", error);
});
//return vm.categories;
}
}]);
Filter will be like this
'use strict';
angular.module('navbar').filter('category', function() {
return function(categories) {
var categoryIds = ['World'];
var result = [];
angular.forEach(categoryIds, function (categoryId) {
angular.forEach(categories, function (category) {
if (category.name == categoryId) {
console.log("Match");
result.push(category);
}
});
});
return result;
};
});
Your filter should be like this and it should be called in transformResponse in $resource query instead of service, i hope this will help you
'use strict';
angular.module('navbar').filter('category', function() {
return function(categories) {
var categoryIds = ['World'];
var result = [];
angular.forEach(categoryIds, function (categoryId) {
angular.forEach(categories, function (category) {
if (category.name == categoryId) {
console.log("Match");
result.push(category);
}
});
});
return result;
};
});
Your categoryResource.service should be like this
angular.module('shared').factory('categoryResourceService',
['$resource','categoryFilter', function($resource, categoryFilter) {
var provider = "categories.json";
var params = {
id: '#id'
};
return $resource(provider, params, {
query: {
isArray: true,
method: 'GET',
params: {},
transformResponse: function(categories) {
var results = categoryFilter(angular.fromJson(categories));
console.log("[categoryResourceService] filtered response:", results);
return results;
}
}
});
}]);
navbar.service should be like this simply
'use strict';
angular.module('navbar')
.service('navbarService', [ 'categoryResourceService', function (categoryResourceService) {
var vm = this;
vm.getCategories = function(){
vm.categories = categoryResourceService.query(function(response){
console.log("[NavbarService] response:", response);
}, function(error){
console.log("[NavbarService] error:", error);
});
return vm.categories;
}
}]);
And components like this
'use strict';
angular.module('navbar').component('appNavbar', {
templateUrl: "navbar.template.html",
controller: [ 'navbarService', function appNavbarController(navbarService) {
var vm = this;
vm.categories = navbarService.getCategories();
}]
});
Given the following test.
The $provided service is not being injected. If I debug the test in karma I can see that the service being provided is the real one, and not the mock.
The really weird thing, is that if I remove the $provide.service... I get an error Error: [$injector:unpr] Unknown provider: ficaServiceProvider <- ficaService. This clearly means that the service is getting registered, just not replaced?
describe("component: FicaStatusComponent",
function () {
var fs;
beforeEach(function () {
module("aureus",
function ($provide) {
$provide.service("ficaService", function () {
this.status = function () {
return $q(function (resolve, reject) {
resolve([{ documentType: { id: 1 } }]);
});
}
})
});
});
beforeEach(inject(function (_$componentController_, _ficaService_) {
$componentController = _$componentController_;
fs = _ficaService_;
}));
it("should expose a `fica` object", function () {
console.log('should expose');
var bindings = {};
var ctrl = $componentController("ficaStatus", null, bindings);
expect(ctrl.fica).toBeDefined();
});
it("compliant with no documents should not be compliant",
function () {
var ctrl = $componentController("ficaStatus");
expect(ctrl.fica.length).toEqual(1);
});
}
);
The second test compliant with no documents... is failing.
Chrome 56.0.2924 (Windows 10 0.0.0) component: FicaStatusComponent compliant with no documents should not be compliant FAILED
Error: Unexpected request: GET api/fica/status/
I have also tried this, expecting to have an empty object injected, but the "real" service is there nevertheless?
module("aureus", function($provide) {
$provide.value("ficaService", function() { return {}; });
$provide.service("ficaService", function() { return {}; });
});
Here is the implementation of the controller for the component:
var FicaStatusController = (function () {
function FicaStatusController($log, $loc, ficaService) {
var _this = this;
this.$log = $log;
this.$loc = $loc;
this.ficaService = ficaService;
this.fica = [];
this.ficaService.status(1234).then(function (_) { return _this.fica = _; });
}
FicaStatusController.$inject = ["$log", "$location", "IFicaStatusService"];
module("aureus").component("ficaStatus", new FicaStatusComponent());
module("aureus").service("IFicaStatusService", FicaStatusService);
The service is as follows:
var FicaStatusService = (function () {
function FicaStatusService($log, $http) {
this.$log = $log;
this.$http = $http;
}
FicaStatusService.prototype.status = function (accountNumber) {
var url = "api/fica/status/" + accountNumber;
this.$log.log("status: " + url);
return this.$http
.get(url)
.then(function (_) { return _.data; });
};
return FicaStatusService;
}());
...
You have added your service in your module like this:
module("aureus").service("IFicaStatusService", FicaStatusService);
That means that you will need to provide IFicaStatusService instead of ficaService with $provide.
After building the Web API, I just promoted a controller to a service (.factory) to share that data with other controllers in AngularJS.
However it seems that I have a syntax error and the factory is not accepted as it.
This is my App.js:
//appContacts.js
(function () {
"use strict";
var app = angular.module("appContacts", ["simpleControls", 'ui.router', 'ngResource'])
.config(a lot of stuff here)
.factory('ContactService', function ($resource) {
var ContactService = {};
var vm = this;
vm.contacts = [];
vm.newContact = {};
vm.errorMessage = "";
vm.isBusy = true;
// Load all contacts
ContactService.getContacts = function () {
return $resource.get("api/contacts")
.then(function (response) {
angular.copy(response.data, vm.contacts);
}
);
};
//Set Current Contact
ContactService.setCurrentContact = function (contact) {
vm.currentContact = contact;
return vm.currentContact;
};
return ContactService;
})
})();
and this is my contatcsController:
//contactsController.js
(function () {
"use strict";
angular.module("appContacts")
.controller("contactsController", function (ContactService) {
//Get all Contacts
ContactService.getContacts = function () {
vm.contacts = contacts;
}
vm.setCurrentContact = function (contact) {
vm.currentContact = contact;
};
})
})();
I keep getting this error: Possibly unhandled rejection: {} in the Console and nothing shows in the front end.
Does anybody has an idea why it does not work as factory the same code that worked inside the controller?
More details at https://github.com/angular-ui/ui-router/issues/2889
app.config(['$qProvider', function ($qProvider) {
$qProvider.errorOnUnhandledRejections(false);
}]);
I'm following John Papa's Angular Style guide for creating a small application, and I can't seem to work out an issue with the controller using a method from a service...
module
(function () {
'use strict';
var accountsApp = angular.module('accountsApp', ['ui.bootstrap', 'ngAnimate']);
})();
Controller
(function() {
'use strict';
angular
.module('accountsApp')
.controller('accountsCtrl', accountsCtrl);
accountsCtrl.$inject = ['$log'];
function accountsCtrl($log, accountsDataSvc) {
/* jshint validthis:true */
var vm = this;
vm.title = 'Accounts';
vm.accounts = [];
vm.accountForm = null;
vm.account = { id: 0, name: '', description: '' }
activate();
function activate() {
return getAccounts(1).then(function() {
$log.info('Accounts loaded');
});
}
function getAccounts(id) {
return accountsDataSvc.getAccounts(id).then(function(data) { //error here
vm.accounts = data;
return vm.accounts;
});
}
}
})();
Service
(function () {
'use strict';
angular
.module('accountsApp')
.factory('accountsDataSvc', accountsDataSvc);
accountsDataSvc.$inject = ['$http', '$log'];
function accountsDataSvc($http, $log, $q) {
var uri = '***';
var service = {
getAccounts: accounts
};
return service;
//////////////////////
function accounts(userId) {
$log.info(uri + userId + ' called');
return $http.get(uri + userId)
.then(getAccountsComplete)
.catch(function (message) {
$log.error('XHR Failed for getAccounts ' + message);
});
function getAccountsComplete(data, status, headers, config) {
return data.data[0].data.results;
}
}
}
})();
When I run the application, I get the error TypeError: Cannot read property 'getAccounts' of undefined within the controller, but I can't see where I have gone wrong - any assistance would be much appreciated.
It looks like you forgot to inject accountsDataSvc into your controller
Try:
accountsCtrl.$inject = ['$log', 'accountsDataSvc'];
I'm newbie in angularjs and I'm trying to create new provider. This is my code:
myApp.provider('$Data', function() {
this.URL = 'http://maps.googleapis.com/maps/api/geocode/json?address=Singapore, SG, Singapore, 153 Bukit Batok Street 1&sensor=true';
this.$get = $get;
$get.$inject = ['$http', '$q'];
function $get($http, $q) {
var that = this;
return {
isConnected: function() {
var bIsConnected = 'Default';
$http({method: 'GET', url:that.URL}).then(function (data) {
bIsConnected = 'Yes';
alert('Run this code!');
}, function (data) {
bIsConnected = 'No';
});
return bIsConnected;
}
}
}
});
Jsfiddle demo:
http://jsfiddle.net/0udm9/9dPsb/6/
After I run $Data.isConnected(), the result is always 'Default' although browser show the alert box. I think it's from success function is not of $get. And I have to use provider, not service or factory for this case. Can I do anything to fix this issue?
Thanks,
You have to use promise in your code.
DEMO
Provider:
isConnected: function() {
var deferred = $q.defer();
$http.get(that.url).then(function(res) {
deferred.resolve('Yes');
console.log('example:success', res);
}, function(err) {
deferred.resolve('No');
console.log('example:error', err);
});
return deferred.promise;
}
Controller:
$Data.isConnected().then(function(data) {
$scope.data = data;
});
// UPD
You must use objects if you need to use return values with async code.
DEMO
// UPD 2
FRESH DEMO LINK