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;
});
Related
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 am having a controller and service like below
(function () {
var mockController = function ($scope, MockService) {
$scope.message = "This is a text message";
$scope.getCities = function () {
$scope.places = [];
MockService.getCities().then(function (response) {
var places = response.data["weather-app:root"].city;
if (places) {
if (Array.isArray(places)) {
$scope.places = places;
} else {
$scope.places.push(places);
}
}
});
};
};
var mockService = function ($http) {
this.getCities = function () {
return $http.get("../rest/url", {
headers: {
'Accept': 'application/yang.data+json'
}
});
};
};
angular.module("MockApp", [])
.service("MockService", mockService)
.controller("MockController", mockController);
}())
I created a mock service like below for mocking the service for unit testing.
(function () {
angular.module('mock.service', [])
.service('MockService', function ($q) {
var mockService = {};
mockService.getCities = function () {
var mydata = {
"weather-app:root": {
"city": [
{
"city-name": "Chennai"
, "country-name": "India"
}
, {
"city-name": "Mangalore"
, "country-name": "India"
}
]
}
}
return $q.when(mydata);
};
return mockService;
});
}());
My test case is like
describe("MockController", function () {
var $scope;
beforeEach(function () {
module("MockApp");
beforeEach(module('mock.service'));
inject(function (_$controller_, _$rootScope_, _MockService_) {
$scope = _$rootScope_.$new();
controller = _$controller_("MockController", {
$scope: $scope
, MockService: _MockService_
});
});
});
describe("Test", function () {
it("Should be Bangalore", function () {
$scope.getCities();
console.log($scope.places);
});
});
});
the problem is that the then method in controller is not getting called. How can I resolve the issue ?
Three things to fix...
Don't nest the beforeEach calls. You can init multiple modules with module.
beforeEach(function() {
module('MockApp', 'mock.service');
// and so on
Your mock data does not quite match what you'd see from an $http based promise response
return $q.when({data: mydata});
In order to process promises, you need to trigger a digest cycle
it("Should be Bangalore", function() {
$scope.getCities();
$scope.$apply();
console.log($scope.places);
});
I have the following controller:
(function () {
"use strict";
angular.module('usp.configuration').controller('ConfigurationController', ConfigurationController);
ConfigurationController.$inject = ['$scope', '$rootScope', '$routeParams', 'configurationService'];
function ConfigurationController($scope, $rootScope, $routeParams, configurationService) {
//Get Master Gas List
configurationService.getMasterGasList().then(function (response) {
$scope.masterGasList = response.data.data;
});
$scope.convertToInt = function (str) {
if (!isNumberEmpty(str) && !isNaN(str)) {
return parseInt(str, 10);
}
return "";
}
$scope.convertToString = function (num) {
if (!isNumberEmpty(num) && !isNaN(num)) {
return num + "";
}
return "";
}
}
}());
And below is the test case for the controller:
describe("test suite for Configuration test controller", function() {
var scope = null;
var configurationService;
beforeEach(module("usp.configuration"));
beforeEach(inject(function($rootScope, $controller, _configurationService_) {
// Services
// _'s are automatically unwrapped
configurationService = _configurationService_;
// Controller Setup
scope = $rootScope.$new();
$controller("ConfigurationController", {
$scope: scope,
configurationService : configurationService
});
}));
it("should convert to int", function() {
expect(scope.convertToInt("2")).toEqual(2);
});
it("should return empty string", function() {
expect(scope.convertToInt("asd")).toEqual("");
});
});
I don't want to call that service while I am running the test case.
I am new to unit testing, I don't know how can I do this.
Please help me to do this?
You need to mock the dependencies with $provide
beforeEach(function () {
configurationServiceMock = {
getSomething: function () {
return 'mockReturnValue';
}
};
module(function ($provide) {
$provide.value('configurationService', configurationServiceMock);
});
});
see: Injecting a mock into an AngularJS service
Solution for your needs:
var configurationServiceMock = {
getMasterGasList: function () {
return {
then: function(callback) {}
};
}
};
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('ConfigurationController', {
'$scope': scope,
'configurationService': configurationServiceMock
});
}));
This is my angular code
var app = angular.module('FleetTask', ['ui.router']);
app.constant('settings', {
ApiURL: 'http://localhost/FleetTask.API',
MsgDuration: 4000,
ProductURL: '/api/product',
ProductGroupURL: '/api/productgroup',
DataAreaURL: '/api/dataarea'
});
app.factory('ProductGroupFactory', ['$http', 'settings', function ($http, settings) {
return {
getProductGroupList: function () {
return $http.get(settings.ApiURL + settings.ProductGroupURL);
},
getProductGroupByID: function (id) {
return $http.get(settings.ApiURL + settings.ProductGroupURL + "/" + id);
},
saveProductGroup: function (productGroup) {
return $http.put(settings.ApiURL + settings.ProductGroupURL, productGroup);
},
deleteProductGroup: function (id) {
return $http.delete(settings.ApiURL + settings.ProductGroupURL + "/" + id);
},
getProductList: function () {
return $http.get(settings.ApiURL + settings.ProductURL);
},
getDataAreaList: function (lookup) {
return $http.get(settings.ApiURL + settings.DataAreaURL + "/dataarealist/" + lookup);
}
};
}]);
app.controller('ProductGroupController', function ($scope, ProductGroupFactory, $rootScope, settings) {
function init(pageIndex) {
$rootScope.pageTitle = "Product Group";
// Complete functionality
};
init();
});
This is my test spec:
describe('FleetTask', function () {
beforeEach(function () {
module('FleetTask');
module('ui.router');
});
describe('ProductGroupFactory', function () {
var scope, controller, productGroupFactory, settings;
beforeEach(inject(function ($injector, $rootScope, $controller, $provide) {
scope = $rootScope.$new();
settings = $injector.get('settings');
productGroupFactory = {
save: jasmine.createSpy()
};
$provide.value('ProductGroupFactory', productGroupFactory);
}));
it('Access Factory', function () {
expect(settings).toBeDefined();
});
});
});
This is the error I am getting
Error says UnKnown provider. Looks like I am not doing right way of injecting factory. 'settings' service got injected correctly, but not the factory. Help me to find out right process to inject factory. Thanks
Answer: Derived test spec I have made out of answers.
describe('FleetTask', function () {
var scope, controller, pgFactory, constants, httpBackend;
beforeEach(function () {
module('FleetTask');
module('ui.router');
module('angulartics');
module('angulartics.google.analytics');
inject(function (_settings_, $injector, $controller, _$rootScope_) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
constants = _settings_;
pgFactory = function () {
return $injector.get('ProductGroupFactory');
};
httpBackend = $injector.get('$httpBackend');
httpBackend.whenGET(constants.ApiURL + constants.ProductURL).respond(function (data) { scope.products = data; });
httpBackend.expectGET(constants.ApiURL + constants.ProductGroupURL);
httpBackend.expectGET(constants.ApiURL + constants.DataAreaURL + "/dataarealist/0");
controller = $controller;
controller('ProductGroupController', { '$scope': scope });
});
});
it('Check if settings service is defined', function () {
expect(constants).toBeDefined();
});
it('Check if ProductGroupFactory is defined', function () {
expect(pgFactory).toBeDefined();
});
it('Check if controller is defined', function () {
expect(controller).toBeDefined();
});
it('Access Controller', function () {
expect(scope.error).toBe('');
});
it('Check if controller is defined', function () {
var prds = scope.products;
var len = prds.length;
expect(len).toBe(0);
});
});
You need to access $provide as a part of module specification, it can't be injected. i.e
beforeEach(module("FleetTask", function ($provide) {
productGroupFactory = jasmine.createSpyObj('productGroupFactory', ['save']);
$provide.value('ProductGroupFactory', productGroupFactory);
}));
beforeEach(inject(function (_settings_, $rootScope, $controller) {
scope = $rootScope.$new();
settings = _settings_;
//...
}));
it('Access Factory', function () {
expect(settings).toBeDefined();
});
I have a factory as follows:
var myFactory = function ($q) {
factory = {};
factory.newObject = function() {
return $q.when({ id: 0 }});
};
};
angular.module('myModule').factory('myFactory', myFactory);
How can I use jasmine to test the newObject function and ensure id returned is 0?
Here's what I have so far:
describe("My Factory Test", function () {
var testMyFactory;
beforeEach(module('myModule'));
beforeEach(inject(function (myFactory) {
testMyFactory = myFactory;
}));
it('New Entity', function ($q) {
testMyFactory.newObject().then(function(myObj) {
expect(myObj.id).toBe(0); // does not work
});
});
});
this worked:
describe("My Factory Test", function () {
var testMyFactory, rootScope;
beforeEach(module('myModule'));
beforeEach(inject(function (myFactory, $rootScope) {
testMyFactory = myFactory;
rootScope = $rootScope;
}));
it('New Entity', function () {
var testObj;
testMyFactory.newObject().then(function(myObj) {
testObj = myObj;
});
rootScope.$apply();
expect(testObj.id).toBe(0);
});
});