Angular: Mocking a Data Service - angularjs

I'm doing unit testing for the first time and I'm trying to work out how to mock a data call from a service so I can test if the data is coming back in the correct form:
My Service
angular.module('app.core')
.factory('PeopleService', PeopleService)
function PeopleService($http, $q, $filter) {
var endpoint;
var service = {
customers: {
value: null
},
getAllCustomers: getAllCustomers,
};
return service;
function getCustomers(endpoint_) {
endpoint = endpoint_;
service.customers.value = [];
return handleFetch($http.get(endpoint));
}
function handleFetch(promise) {
return promise.then(function (resp) {
service.customers.value = service.customers.value.concat(resp.data.data);
});
}
function getAllCustomers() {
return $q.all([
getCustomers('/api/customers'),
]).then(function(responses) {
return responses[0];
});
}
}
My Controller
angular.module('app.people')
.controller('peopleCtrl', peopleCtrl);
function peopleCtrl($scope, PeopleService) {
$scope.customers = PeopleService.customers;
getCustomers();
function getCustomers() {
return PeopleService.getAllCustomers().then(function () {
return PeopleService.customers.value;
});
}
}
My Test
describe('People Service', function () {
var controller;
var customers = mockData.getMockCustomers(); // my fake customers array
beforeEach(function() {
bard.appModule('app');
bard.inject('$controller', '$q', '$rootScope', 'PeopleService');
var ps = {
getAllCustomers: function() {
return $q.when(customers);
}
};
controller = $controller('peopleCtrl', {
$scope: $rootScope,
PeopleService: ps
});
});
it('should return an array of 5 customers', function() {
$rootScope.$apply();
expect($rootScope.customers).to.have.length(5);
});
});
I've got a controller set up that when loaded talks to the People Service and gets my customers and saves the array of customers to PeopleService.customers.value. Inside my controller, I have a variable $scope.customers which is equal to PeopleService.customers.
I'm trying to mock this with my test, without hitting the API, I'm using some mock data to do this (an array of 5 customers), but not sure if I understand correctly.
Is the idea to have my mock people service return exactly what the actual people service returns? I'm kind of confused at this point. I basically want that test to check if the mock data length is equal to five.
Any help with this is appreciated. Thanks in advance!

Related

My Service is returning data to the controller but I cannot seem to access it like I would expect.

(function () {
angular.module("app").controller('DashboardController', ['$q', 'dashboardService', function ($scope, $q,dashboardService) {
var DashboardController = this;
dashboardService.loadFromServer(DashboardController );
console.log("DashboardController ", DashboardController);
}])
})();
angular.module("app").service('dashboardService', ['$http', '$q', function ($http, $q) {
return {
loadFromServer: function (controller) {
var getDashboardEntries = $http.get('http://someUrl');
var getEmailData = $http.get('http://someOtherUrl');
var getSidebarData = $http.get('http://yetAnotherUrl');
return $q.all([getDashboardEntries, getSidebarData, getEmailData])
.then(function (results) {
controller.dashboardData = results[0].data;
controller.chartData = results[1].data;
controller.emailData = results[2].data;
});
},
};
}]);
1.The service returns the three bits of data and this is the results when logged using:
console.log("DashboardController ", DashboardController);
When I try to drill down on the data in this manner it logs "undefined"
console.log("DashboardController "DashboardController.dashboardData);
console.log("DashboardController "DashboardController.chartData);
console.log("DashboardController "DashboardController.emailData);
Do you realize that console.log is executed right after invoking loadFromServer before the server has chance to respond and promise resolves? The actual order is:
loadFromServer
console.log
promise success method - where you actually have your data
Change your controller's code to this:
dashboardService.loadFromServer(DashboardController ).then(function() {
console.log("DashboardController ", DashboardController);
});
What would be even better is to construct some object from parts of responses and assign it in the controller itself - not the service. In current implementation if you wanted to have another controller then service would assign response parts to same fields. I'd propose sth like this:
return $q.all([getDashboardEntries, getSidebarData, getEmailData])
.then(function (results) {
var data = {
dashboardData = results[0].data;
chartData = results[1].data;
emailData = results[2].data;
};
return data;
});
and then in controller:
dashboardService.loadFromServer().then(function(data) {
DashboardController.dashboardData = data.dashboardData;
DashboardController.chartData = data.chartData;
DashboardController.emailData = data.emailData;
});
In this solution the controller decides what to do with data, not the other way around.

pass data between controllers in AngularJS dynamically [duplicate]

This question already has answers here:
Share data between AngularJS controllers
(11 answers)
Closed 2 years ago.
i have tow controller in angularjs. if one controller change data other controller display updated data. in fact first controller has a event that it occur second controller display it. for this propose i wrote a service. this service has tow function. here is my service code.
app.service('sharedData', function ($http) {
var data=[]
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
})
},
getData: function(){
return data;
}
}
});
in first controller
app.controller("FirstController", function ($scope, $http,sharedData)
{
$scope.handleGesture = function ($event)
{
sharedData.setData();
};
});
in second controller:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
data = sharedData.getData();
}
);
in first controller setData work with out any problem but in second controller not work correctly. how to share data dynamically between tow controllers?
You are on the right track with trying to share data between controllers but you are missing some key points. The problem is that SecondController gets loaded when the app runs so it calls sharedData.getData() even though the call to setData in the firstController does not happen yet. Therefore, you will always get an empty array when you call sharedData.getData().To solve this, you must use promises which tells you when the service has data available to you. Modify your service like below:
app.service('sharedData', function ($http, $q) {
var data=[];
var deferred = $q.defer();
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
deferred.resolve(response);
})
},
init: function(){
return deferred.promise;
},
data: data
}
})
And the secondController like this:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
sharedData.init().then(function() {
data = sharedData.data;
});
});
For more info on promises, https://docs.angularjs.org/api/ng/service/$q
You had multiple syntax problems, like service name is SharedData and you using it as SharedDataRange, the service is getting returned before the get function.
What I have done is corrected all the syntax errors and compiled into a plunkr for you to have a look. Just look at the console and I am getting the data array which was set earlier in the setter.
Javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", function ($scope,sharedDateRange)
{
sharedDateRange.setData();
});
app.controller("SecondController", function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
});
app.service('sharedDateRange', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
Working Example
If you want to keep sharedDataRange as the variable name and service name as sharedData have a look at this example
javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", ['$scope','sharedData', function ($scope,sharedDateRange)
{
sharedDateRange.setData();
}]);
app.controller("SecondController", ['$scope','sharedData', function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
}]);
app.service('sharedData', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
You can bind the data object on the service to your second controller.
app.service('sharedData', function ($http) {
var ret = {
data: [],
setData: function () {
$http.get('/getData').success(function(response){
data = response;
});
}
};
return ret;
});
app.controller("FirstController", function ($scope, sharedData) {
$scope.handleGesture = function () {
sharedData.setData();
};
});
app.controller("SecondController", function ($scope, sharedData) {
$scope.data = sharedData.data;
});
What you need is a singleton. The service sharedData needs to be a single instance preferably a static object having a static data member. That way you can share the data between different controllers. Here is the modified version
var app = angular.module('app', []);
app.factory('sharedData', function ($http) {
var sharedData = function()
{
this.data = [];
}
sharedData.setData = function()
{
//$http.get('/getData').success(function(response){
this.data = "dummy";
//})
}
sharedData.getData = function()
{
return this.data;
}
return sharedData;
})
.controller("FirstController", function ($scope, $http,sharedData)
{
sharedData.setData();
})
.controller("SecondController", function ($scope,sharedData) {
$scope.data=sharedData.getData();
});
I have removed the event for testing and removed the $http get for now. You can check out this link for a working demo:
http://jsfiddle.net/p8zzuju9/

How to assign value to variables in factory and test a method using assigned value in AngularJS?

I have following factory. I have changed init method for the sake of brevity, because all that matters is that it loads json data (similar to data in test) in teamsConfig.
var AppController = angular.module("AppController");
AppController.factory('TeamFactory', ['$http', '$q',
function($http, $q) {
var teamsConfig = {};
return{
init: function() {
$http.get('navigation/TeamsConfiguration.json').success(function(data) {
teamsConfig = data;
}).error(function(msg, code) {
deferred.reject(msg);
});
return deferred.promise;
},
getCaptainName: function(team) {
return teamsConfig[team].captainName;
}
};
My Jasmine code is as below
describe('Test TeamCaptain',function(){
beforeEach(module('AppController'));
var ctrl,scope,http,route, routeParams,location,q,svc,factory;
beforeEach(inject(function(TeamFactory,$rootScope){
scope = $rootScope.$new();
TeamFactory.teamsConfig = {
"India":
{
"captain": "Dhoni",
"batsman": "Kohli"
},
"Australia":
{
"captain": "Clarke",
"batsman": "Watson"
}
};
factory = TeamFactory;
}));
it('should get the captain of the team.',function(){
var captain= '';
var team = 'India';
captain = factory.getCaptainName(team);
expect(captain).toEqual('Dhoni');
});
}));
While running tests, I'm getting error
--TypeError: 'undefined' is not an object evaluating 'teamsConfig[team].captainName'
I have changed the code from original, it may have small errors/typos.

Creating a service around Restangular in AngularJS is not returning array

I am new to AngularJS and Restangular. But being a good citizen I thought I would try to create some services.
While this works and I get an array back populating a drop down list:
Restangular.all('/ta/tas').getList().then(function(tas) {
$scope.therapyAreas = tas;
});
But when I create a service, and then inject the service into the controller I get nothing back:
.controller('prGPTCtrl',['$scope', 'prTAService', 'Restangular', function($scope, prTAService, Restangular) {
$scope.prGpts = {};
$scope.therapyAreas = {};
Restangular.setBaseUrl('resources/pr');
$scope.therapyAreas = prTAService.prTAList();
Service:
prServices.factory('prTAService', ['Restangular', '$q', function prTAService(Restangular, $q) {
return {
prTAList : function(therapyAreas) {
var therapyAreas = {};
Restangular.setBaseUrl('resources/pr');
Restangular.all('/ta/tas').getList().then(function(tas) {
therapyAreas = tas;
return therapyAreas;
}
);
}
};
}]);
I see it being called in Chrome Debugger...I suspect it is related to promises (which I don't really "get").
Update:
This works as a service:
prServices.factory('prTAService', ['Restangular', '$q', function prTAService(Restangular, $q) {
return {
prTAList : function(therapyAreas) {
Restangular.setBaseUrl('resources/pr');
return Restangular.all('/ta/tas').getList();
}
};
}]);
And this works when calling it, not sure what I am gaining here!
prTAService.prTAList().then(function(tas) {
$scope.therapyAreas = tas;
});
The service needs to return the promise and the controller will handle the return because this is asynchronous. You are doing it right in the last part there:
prTAService.prTAList().then(function(tas) {
$scope.therapyAreas = tas;
});

How to inject multiple angular services using $inject.get

Im having problem using $inject.get in angular js..
Let say i have angular services like this
app.service("serviceOne", function() {
this.dialogAlert = function() {
return 'Message One'
};
});
app.service("serviceTwo", function() {
this.dialogAlert = function() {
return 'Message Two'
};
});
app.service("serviceThree", function() {
this.dialogAlert = function() {
return 'Message Three'
};
});
And using the factory to dynamically call dialogAlert()
app.factory("alertService", function($window, $injector) {
if ($window.servicesOne) {
return $injector.get("serviceOne");
} else {
return $injector.get(["serviceTwo", "serviceThree"]);
}
});
With this kind of codes, it gives me "unknown provider".
Or is there any alternative solution for this?
Thanks guys.
injector.get takes only one service name as argument, array is not supported, you may want to do return array of service instances by doing return ["serviceTwo", "serviceThree"].map($injector.get):-
app.factory("alertService", function($window, $injector) {
var service = ["serviceOne"];
if (!$window.servicesOne) {
service = ["serviceTwo", "serviceThree"];
}
return service.map($injector.get); //To be consistent send back this as well as array
});
So with this when you inject the alertService it will return an array of dependecy(ies).
app.controller('MainCtrl', function($scope, alertService) {
// alertService will be array of dependecies.
console.log(alertService.map(function(itm){return itm.dialogAlert()}));
});
Demo
or return with a map:-
app.factory("alertService", function($window, $injector) {
var service = ["serviceOne"],
serviceObjs = {};
if (!$window.servicesOne) {
service = ["serviceTwo", "serviceThree"];
}
angular.forEach(service, function(itm){
serviceObjs[itm] = $injector.get(itm);
});
return serviceObjs;
});

Resources