When mocking using $httpBackend, How do I create the mock when I do not know exactly the type of data being returned. I want the http call to be on controller initialsation. One caveat is that it will be a json object being returned.
(function () {
'use strict';
angular
.module('app')
.service('dataService', dataService);
function dataService($http) {
this.getMovies = getMovies;
////////////////
function getMovies() {
return $http.get('./src/app/movies/data.json')
.then(function (response) {
return response.data
})
}
};
})();
;
(function () {
'use strict';
angular.module('app')
.controller('moviesController', moviesController);
moviesController.$inject = ['dataService'];
function moviesController(dataService) {
var vm = this
vm.movies;
vm.getMovies = getMovies;
getMovies();
function getMovies() {
return dataService.getMovies()
.then(function (data) {
return vm.movies = data.movies;
});
}
};
}());
;
describe('moviesController', function () {
var $controller,
moviesController,
dataService,
$httpBackend;
beforeEach(angular.mock.module('app'));
beforeEach(angular.mock.module('ui.router'));
beforeEach(inject(function (_$controller_, _dataService_, _$httpBackend_) {
$controller = _$controller_;
dataService = _dataService_;
$httpBackend = _$httpBackend_;
moviesController = $controller('moviesController', { dataService: dataService });
}))
it('should be defined', function () {
expect(moviesController).toBeDefined();
});
it('should initialise with a call to dataService.getMovies()', function () {
var url = "./src/app/movies/data.json";
var movies = {};
$httpBackend.expectGET(url).respond(200, movies);
moviesController.getMovies();
expect(moviesController.movies).toEqual(movies);
$httpBackend.flush();
});
});
;
Expected undefined to equal Object({ }).
You can set the return to be an object you define in the spec.
var response = {};
it('should initialise with a call to dataService.getMovies()', function () {
$httpBackend.expectGET("./src/app/movies/data.json").respond(response);
$httpBackend.flush();
});
Related
I am writing unit tests for my existing AngularJS app. There are just four methods in this service. I was able to getFollowUpList to work, but refresh() is not working and it is a very simple method.
The refresh method should simply set deferredGetFollowUpList = null and return true in my test.
There error I'm getting is: TypeError: Cannot read property 'then' of undefined, so my refresh method is undefined. Why is this the case? Thanks
Service
(function () {
"use strict";
angular
.module("all.patient.details")
.factory("followUpListService", ["$rootScope", "$http", "userContext", "$q", "$uibModal", "htmlBaseUrl", FollowUpListService]);
function FollowUpListService($rootScope, $http, userContext, $q, $uibModal, htmlBaseUrl) {
var deferredGetFollowUpList = null;
return {
getFollowUpList: getFollowUpList,
displayModal: displayModal,
refresh: refresh,
save: save
}
function refresh() {
deferredGetFollowUpList = null;
}
}
})();
Unit Test
describe("followUpListService", function () {
beforeEach(module("all.patient.details"));
var followUpListService = {};
var $httpBackend;
var htmlBaseUrlMock;
var returnedFollowUpListData;
var deferredGetFollowUpList;
var $rootScope;
var $q;
var $uibModal;
beforeEach(function () {
htmlBaseUrlMock = { format: function () { } };
module(function ($provide) {
$provide.value("htmlBaseUrl", htmlBaseUrlMock);
});
inject(function (_$rootScope_, _$httpBackend_, _$q_, _$uibModal_, _followUpListService_) {
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
$q = _$q_;
$uibModal = _$uibModal_;
followUpListService = _followUpListService_;
});
});
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("calls refresh()", function () {
followUpListService.refresh()
.then(function (data) {
deferredGetFollowUpList = data;
});
expect(deferredGetFollowUpList).toBe(null);
});
As deferredGetFollowUpList is service level variable, Can you write your test as -
followUpListService.deferredGetFollowUpList = data; //Any Mock Data
followUpListService.refresh();
expect(followUpListService.deferredGetFollowUpList).toBe(null);
I'm trying to figure out these karma tests to test my Angular app. I can figure out simple things. But I am having problems with one that connects to my database.
I am wanting to test the $scope.getMultipleOptions function in the controller below:
$scope.options = [];
$scope.getMultipleOptions("form_field_1", $scope.options);
$scope.getMultipleOptions = function (key, opts) {
var id = key.replace("form_field_", "");
var promise = DynamicFormFactory.GetData('/DynamicForm/GetMultipleOptions?form_field_id=" + id);
promise.then(
function (success) {
angular.forEach(success, function (o) {
opts.push(o);
});
},
function (error) {
// Error
}
);
}
This is what my factory/service looks like:
dynamicFormApp.factory('DynamicFormFactory', ['$http', function ($http) {
return {
GetData: function (url) {
return $http.get(url)
.then(function (response) {
return response.data;
}, function (error) {
return error;
});
}
}]);
And this is what I've done to set up my karma test
describe('DynamicFormController', function () {
beforeEach(module('dynamicFormApp'));
var $controller;
var $rootScope;
beforeEach(inject(function (_$controller_, _$rootScope_) {
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('$scope.getMultipleOptions', function () {
var $scope, controller;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('DynamicFormController', { $scope: $scope });
$scope.fields = [];
});
it('$scope.getMultipleOptions', function () {
var key = "form_field_15";
var expectedResult = [{ desc: "DESCRIPTION", id: "ID" }];
$scope.getMultipleOptions(key, $scope.fields);
expect($scope.fields).toEqual(expectedResult);
});
});
});
The test always fails as $scope.fields is always empty. How do I do this?
I'm trying to write a unit test using karma and jasmine for an Angular controller that depends on a service
storesController.js
(function () {
var app = angular.module('storesController', ['storesService']);
app.controller('StoresListController', function ($scope, StoresService) {
$scope.getStores = function () {
StoresService.getStores().then(function (data) {
$scope.stores = data.data;
});
};
$scope.getStores();
$scope.deleteStore = function (id) {
StoresService.deleteStore(id).then(function () {
$scope.getStores();
});
};
});
storesService.js
(function () {
var app = angular.module('storesService', []);
app.factory('StoresService', ['$http', function ($http) {
var stores = [];
stores.getStores = function () {
return $http.get(/api/getStores');
};
stores.deleteStore = function (storeID) {
return $http.delete(/api/deleteStore/'+storeID);
};
return stores;
}]);
})();
And the test,
controllers.spec.js
describe('StoresController', function () {
beforeEach(module('storesController'));
var scope;
var storesServiceMock;
var controller;
beforeEach(inject(function ($controller, $rootScope) {
storesServiceMock = {
getStores: function() {
},
deleteStores: function() {
}
};
spyOn(storesServiceMock, 'getStores').and.returnValue({name : 'TestName', country : 'TestCountry'})
scope = $rootScope.$new();
controller = $controller('StoresListController', {
$scope: scope, StoresService: storesServiceMock
});
}));
it('scope.stores should be defined', function () {
expect(scope.stores).toBeDefined;
});
});
And I'm getting
TypeError: StoresService.getStores(...).then is not a function at n.$scope.getStores
I've also tried width httpBackend but I'm not be able to make it work, any clue about what I'm doing wrong?
Have the spy return a promise.
With ES2015:
spyOn(storesServiceMock, 'getStores').and.returnValue(Promise.resolve({name : 'TestName', country : 'TestCountry'}));
With $q:
spyOn(storesServiceMock, 'getStores').and.callFake(function() {
var deferred = $q.defer();
deferred.resolve({name : 'TestName', country : 'TestCountry'}));
return deferred.promise;
});
I'm trying to mock a service I'm using and should return a promise, the mock service is being called but I can't get the result to my test.
service function to be tested:
function getDevices() {
console.log('getDevices');
return servicesUtils.doGetByDefaultTimeInterval('devices')
.then(getDevicesComplete);
function getDevicesComplete(data) {
console.log('getDevicesComplete');
var devices = data.data.result;
return devices;
}
}
My test is:
describe('devicesService tests', function () {
var devicesService;
var servicesUtils, $q, $rootScope;
beforeEach(function () {
servicesUtils = {};
module('app.core', function ($provide) {
servicesUtils = specHelper.mockServiceUtils($provide, $q, $rootScope);
});
inject(function (_devicesService_, _$q_, _$rootScope_) {
devicesService = _devicesService_;
$q = _$q_;
$rootScope = _$rootScope_.$new();
});
});
it('getting device list', function () {
console.log('getting device list');
devicesService.getDevices().then(function (result) {
console.log(result);
expect(result).toBeDefined();
});
});
});
Mock file:
function mockServiceUtils($provide, $q) {
var servicesUtils = {};
servicesUtils.doGetByDefaultTimeInterval = jasmine.createSpy().and.callFake(function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
$rootScope.$digest();
return deferred.promise;
});
$provide.value('servicesUtils', servicesUtils);
return servicesUtils;
}
Your code is way too complex.
Let's assume that you want to test a service devicesService that uses another service servicesUtils, having a method that returns a promise.
Let's assume devicesService's responsibility is to call servicesUtils and transform its result.
Here's how I would do it:
describe('devicesService', function() {
var devicesService, servicesUtils;
beforeEach(module('app.core'));
beforeEach(inject(function(_devicesService_, _servicesUtils_) {
devicesService = _devicesService_;
servicesUtils = _servicesUtils_;
}));
it('should get devices', inject(function($q, $rootScope) {
spyOn(servicesUtils, 'doGetByDefaultTimeInterval').and.returnValue($q.when('Remote call result'));
var actualResult;
devicesService.getDevices().then(function(result) {
actualResult = result;
});
$rootScope.$apply();
expect(actualResult).toEqual('The transformed Remote call result');
}));
});
I am trying to test a controller method that relies on a service call to get some data. The service method returns a promise, and I'd like to test the behavior of the controller if the promise is resolved or rejected.
I have come up with this approach to vary the behavior of my mocked service method, but it does not work. The getDataSuccess flag is always true when the mocked getData method is called. Here's what I have so far:
Controller:
app.controller('myController', function($scope, myService) {
myService.getData()
.then(function (data) {
$scope.data = data;
},
function (data) {
$scope.serverError = data;
});
});
Test:
describe('myController', function () {
var ctl, serviceMock, getDataSuccess, scope;
beforeEach(function() {
getDataSuccess = true;
serviceMock = {};
module('app', function ($provide) {
$provide.value('myService', serviceMock);
});
inject(function ($q) {
serviceMock.getData = function () {
var defer = $q.defer();
if (getDataSuccess) {
defer.resolve("theData");
} else {
defer.reject("theData");
}
return defer.promise;
};
});
});
beforeEach(inject(function ($rootScope, $controller, $httpBackend, myService) {
scope = $rootScope.$new();
ctl = $controller('myController', {
$scope: scope,
myService: myService,
});
}));
describe('myController loading data', function () {
it('should set $scope.data if data load succeeds', function () {
getDataSuccess = true;
scope.$apply();
expect(scope.data).toEqual("theData");
});
it('should set $scope.serverError if data load fails', function () {
getDataSuccess = false;
scope.$apply();
expect(scope.serverError).toEqual("theData");
});
});
});
Clearly I'm missing something here. The order of execution is not what I was expecting. What's the proper way to do this sort of thing?
Here's this example in Plunker: http://plnkr.co/edit/ODyslivLorjaLM4EqlEF?p=preview
myService.getData function is called where myController is initialized. So if you want to change the behavior getData function by setting getDataSuccess, you need to initialize myController after you set getDataSuccess true/false.
What I recommend is something like this.
In appSpec.js
describe('myController', function () {
var ctl, serviceMock, getDataSuccess, scope;
beforeEach(function() {
getDataSuccess = true;
serviceMock = {};
module('app', function ($provide) {
$provide.value('myService', serviceMock);
});
inject(function ($q) {
serviceMock.getData = function () {
var defer = $q.defer();
if (getDataSuccess) {
defer.resolve("theData");
} else {
defer.reject("theData");
}
return defer.promise;
};
});
});
beforeEach(inject(function ($rootScope, $controller, $httpBackend, myService) {
scope = $rootScope.$new();
//
// ctl = $controller('myController', {
// $scope: scope,
// myService: myService,
// });
}));
describe('myController loading data', function () {
it('should set $scope.data if data load succeeds', inject(function($controller, myService){
getDataSuccess = true;
ctl = $controller('myController', {
$scope: scope,
myService: myService,
});
scope.$apply();
expect(scope.data).toEqual("theData");
}));
it('should set $scope.serverError if data load fails', inject(function($controller, myService){
getDataSuccess = false;
ctl = $controller('myController', {
$scope: scope,
myService: myService,
});
scope.$apply();
expect(scope.serverError).toEqual("theData");
}));
});
});
This is updated plunker.