Angular karma testing controller - angularjs

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?

Related

Karma - Unit Testing Angular Controller with Mocked API

I wrote some unit tests for an angular factory that mock the response with a fake promise using $q.resolve({}). I'm now writing the controller test and want to call the factory from the controller and assign the resolved mock factory response to the controller scope (vm), and verify the data. Is this the proper way to test a factory from within a controller?
factory test
describe('getVICs()', function() {
var vicFactory, $q, $httpBackend, result;
beforeEach(module('vicModule'));
beforeEach(inject(function(_vicFactory_, _$q_, _$httpBackend_) {
vicFactory = _vicFactory_;
$q = _$q_;
$httpBackend = _$httpBackend_;
}));
beforeEach(function() {
result = {};
spyOn(vicFactory, 'getVICs').and.callThrough(function() {
var deferred = $q.defer();
return deferred.promise;
});
});
it('should be defined', function() {
expect(vicFactory.getVICs()).toBeDefined();
});
it('should return list of vics', function() {
var vicList = {
"results": [
{
"version": 5
},
{
"version": 1
}
]
};
var qs = '?sort=version&dir=DESC&page=1&limit=10&status=ACTIVE';
$httpBackend.expectGET('/path/to/my/api' + qs).respond(200, vicList);
$httpBackend.whenGET('http://localhost:3000/path/to/my/api' + qs).respond(200, $q.when(vicList));
vicFactory.getVICs(qs)
.then(function(response) {
result = response;
});
// flush pending HTTP requests
$httpBackend.flush();
expect(result.data.results).toBeDefined();
});
});
factory
function getVICs(query) {
return $http.get('/path/to/my/api' + query)
.then(function(response) {
return response;
});
}
controller test
describe('vicController', function() {
var $controller, vicController, vicFactory, $q, $scope, $httpBackend, deferred;
var vicList = {
"results": [
{
"version": 5
},
{
"version": 1
}
]
};
var qs = '?sort=version&dir=DESC&page=1&limit=10&status=ACTIVE';
beforeEach(module('vicModule'));
beforeEach(inject(function(_$controller_, _vicFactory_, _$q_, _$rootScope_, _$httpBackend_, _$httpParamSerializer_) {
$controller = _$controller_;
vicFactory = {
getVICs: function () {
deferred = $q.defer();
return deferred.promise;
}
};
$q = _$q_;
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
spyOn(vicFactory, 'getVICs').and.callThrough();
vicController = $controller('vicController', {vicFactory: vicFactory});
deferred.resolve(vicList);
}));
it('should be defined', function() {
expect(vicController).toBeDefined();
});
it('should call getVICs() on load', function() {
$httpBackend.expectGET(''/path/to/my/api?' + qs).respond(200, vicList);
$scope.$digest();
expect(vicFactory.getVICs).toHaveBeenCalledWith(qs);
expect($scope.vicList).toEqual(vicList);
});
});
controller
angular.module('vicModule')
.controller('vicController', vicController);
vicController.$inject = [
'vicFactory'
];
function vicController(vicFactory) {
var vm = this;
var qs = '?sort=version&dir=DESC&page=1&limit=10&status=ACTIVE';
vicFactory.getVICs(qs)
.then(function(response) {
console.log('vic controller success');
console.log(response);
vm.vicList = response;
}, function(response) {
console.log('vic controller error');
console.log(response);
});
}
Fixed
Got this working by injecting $q and $scope, setting vicFactory in beforeEach, updating spy, and then resolving the deferred with the expected data set after controller initialization. The controller test spec has been updated, hope this help someone.

Mocking $httpBackend on controller initialisation with unknown json data

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();
});

Angular unit testing Controller with service

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;
});

Testing with $http.backend in Angular

I have the following controller:
(function () {
angular.module('cacApp')
.controller('mainController', function ($scope, $location, geonames) {
$scope.browseCountries =
function (path) {
$location.path(path);
};
geonames.countries()
.then(function (response) {
$scope.countries = response;
})
})
}());
and have set up the following tests for it:
describe("mainController", function () {
var _geonames, _location, _scope;
beforeEach(module('cacApp'));
beforeEach(inject(function ($controller, $injector, $httpBackend) {
_geonames = $injector.get("geonames");
_location = $injector.get("$location");
_scope = $injector.get("$rootScope").$new();
httpBackend = $httpBackend;
spyOn(_location, "path");
$controller('mainController', {
$scope: _scope,
$location: _location,
geonames: _geonames
})
}))
it("should equal a length of 250", function () {
var url = 'http://api.geonames.org/countryInfoJSON?username=steveyz';
var httpResponse = [{}];
httpBackend.expectGET(url).respond(200, httpResponse);
expect(_geonames.countries.length).toBe(250);
})
})
The "$scope.countries" response from running geonames.countries() returns an array of 250 objects, and a $scope.countries.length of 250.
I want to test to verify that the length of the response is actually 250 in my code. But when I run the test currently, I get the error "Expected 0 to be 250."
Is there a specific reason why my result is showing 0 instead of 250?

can't get promise result from angular service

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');
}));
});

Resources