I am trying to test a function which is a private function, and it is been called in some other function in my controller. When I try to test this validateParameterGroup function, it gives an error saying that validateParameterGroup is not defined.
controller
angular.module('PpmApp')
.controller('parameterGroupListController', ['$scope', '$injector', 'parameterGroups', parameterGroupListController]);
function parameterGroupListController($scope, $injector, parameterGroups) {
$scope.createParameterGroup = function (parameterGroup) {
var validationErrors = validateParameterGroup(parameterGroup);
}
function validateParameterGroup(parameterGroup) {
// ...
}
};
Test Case
describe('validateParameterGroup', function () {
beforeEach(function () {
var parameterGroup = {};
});
it('should validate a parameter group', function () {
expect(validateParameterGroup(parameterGroup)).toEqual(false);
});
});
============ Edit ==================
If it is not possible to test a private function, Can I test $scope.createParameterGroup? I tried doing this but I am getting following error.
TypeError: $scope.createParameterGroup(...) is not a function
Test
describe('createParameterGroup', function() {
var validationErrors, parameterGroup;
beforeEach(function() {
validationErrors = {};
validationErrors.isError;
parameterGroup = {
GroupName: "ABC",
Description: "ABC",
fromMonth: 1,
fromYear: 18,
toMonth: 12,
toYear: 18
}
});
it('should create a parameter group', function() {
expect($scope.createParameterGroup(parameterGroup)(validationErrors.isError)).toEqual(false);
});
});
Yes, validateParameterGroup becomes fully private and not accessible from outside. You can extend $scope object to include this function to become public, similar to createParameterGroup
angular.module('PpmApp')
.controller('parameterGroupListController', ['$scope', '$injector', 'parameterGroups', parameterGroupListController]);
function parameterGroupListController($scope, $injector, parameterGroups) {
$scope.createParameterGroup = function (parameterGroup) {
var validationErrors = validateParameterGroup(parameterGroup);
}
$scope.validateParameterGroup = function(parameterGroup) {
// ...
}
};
Related
I am writing the UT for controller and while trying to implement for commandRouter.execute method(please refer 2nd spec) , I am getting error message : can not read property 'execute' of undefined.
Can someone let me know what am i doing wrong here and whats the correct way to spy on a method from controller. ?
module.controller('DcsPlus.AP.OmsControl.omsMasterRecipeDialogPopUpController', omsMasterRecipeDialogPopUpController);
omsMasterRecipeDialogPopUpController.$inject = [
'DcsPlus.Frame.Logic.commandRouter'
];
function omsMasterRecipeDialogPopUpController(commandRouter) {
var vm = this;
vm.execute = function(command) {
commandRouter.execute(command);
};
}
controller.spec.js
describe('omsMasterRecipeDialogPopUpController', function () {
var omsMasterRecipeDialogPopUpControllerTest;
var commandRouterMock;
var $scope;
beforeEach(function () {
registerMockServices();
prepareCommandRouterMock();
});
describe('execute', function () {
it('1. Should check if execute method is defined', function() {
expect(omsMasterRecipeDialogPopUpControllerTest.execute).toBeDefined();
});
it('2. Should check if execute method of commandRouter is called', function() {
omsMasterRecipeDialogPopUpControllerTest.execute();
expect(commandRouterMock.execute).toHaveBeenCalled();
});
});
function prepareCommandRouterMock() {
commandRouterMock = {
execute: function() {
}
};
}
/*beforeEach(function () {
commandRouterMock = jasmine.createSpyObj('DcsPlus.Frame.Logic.commandRouter', ['execute']);
});*/
function registerMockServices() {
angular.mock.module('DcsPlus.AP.OmsControl', function ($provide) {
$provide.value('DcsPlus.Frame.Logic.commandRouter', commandRouterMock);
});
angular.mock.inject(['$controller', '$rootScope', 'dialogService',
function ($controller, $rootScope, dialogService) {
$scope = $rootScope.$new();
spyOn(commandRouterMock, 'execute').and.callThrough();
// Init the controller, passing our spy service instance
omsMasterRecipeDialogPopUpControllerTest = $controller('DcsPlus.AP.OmsControl.omsMasterRecipeDialogPopUpController', {
$scope: $scope
});
}]);
}
});
In the beginning you create commandRouterMock but never assign it to anything.
Try this:
beforeEach(function () {
registerMockServices();
commandRouterMock = prepareCommandRouterMock();
});
function prepareCommandRouterMock() {
return {
execute: function() {
}
};
}
I've searched the internet but didn't find a solution that works for me. The situation is that I have a component with a controller. I can use Jasmine to test that the controller is created and I can also call the $onInit hook without a problem.
However, when I try to call a component function that uses a service function that returns a promise, things fail miserably. I'm using the following versions:
AngularJS v1.5.11
Jasmine v3.3.0
Karma v3.1.4
My simplified component is:
(function ()
{
"use strict";
angular
.module("app")
.component("MyComponent", {
controller: "MyController"
})
.controller("MyController", MyController);
function MyController(MyService)
{
"ngInject";
var vm = this;
vm.$onInit = $onInit;
vm.onPaginate = onPaginate;
function $onInit()
{
vm.data = [];
}
function onPaginate()
{
getData();
}
function getData()
{
MyService.getData().then(onComplete);
function onComplete(response)
{
vm.data = response.list;
}
}
}
})();
The function MyService.getData queries a REST API to retrieve some data. It will return a promise that gets resolved when the data is received. This all works in the application.
I want to test the MyController.onPaginate function and wrote the following Jasmine test spec:
describe("MyComponent: MyController", function ()
{
var $componentController, $rootScope, $q, $scope;
var componentBindings = {
};
var data = {
list: [{
"id": 23,
}],
total_count: 1
};
var mock_MyService = {
getData: function ()
{
return $q.resolve(data);
}
};
beforeEach(function ()
{
angular.mock.module("app", function ($provide)
{
$provide.value("MyService", mock_MyService);
});
angular.mock.inject(function ($injector)
{
$q = $injector.get("$q");
$componentController = $injector.get("$componentController");
$rootScope = $injector.get("$rootScope");
$scope = $rootScope.$new();
});
});
it("should retrieve data", function ()
{
var $ctrl = $componentController("MyComponent", { $scope: $scope, MyService: mock_MyService }, componentBindings);
$ctrl.$onInit();
$ctrl.onPaginate($ctrl.tableState);
$scope.$apply();
expect($ctrl.data.length).toEqual(1);
});
});
When I run this I get an error:
TypeError: e is not a function
at <Jasmine>
at b.$apply (node_modules/angular/angular.min.js:147:388)
at UserContext.<anonymous> (myComponent.spec.js:...)
at <Jasmine>
So how can I test this properly?
I'm trying to unit test a controller with a service injected into it. No matter what I seem to try, I get an error. Any assistance to help me get this going would be much appreciated. I'm using Angular/Karma/Jasmine to get run my tests.
There seem to be a lot of posts with similar stories but this feels like it may not be a duplicate - apologies if it is.
My controller looks like this:
(function() {
angular
.module('blah')
.controller('AdminController', AdminController);
/* #ngInject */
function AdminController($scope, toastr, adminService) {
activate();
/**
* Controller initialisation.
*/
function activate() {
getAllUsers();
}
/**
* Gets all users.
*/
function getAllUsers() {
adminService.getAllUsers()
.then(function(response) {
$scope.users = response.data;
})
.catch(function(error) {
toastr.error('Unable to load users', 'Error');
console.log(error);
});
}
}
})();
And my service looks like this:
(function() {
angular
.module('blah')
.factory('adminService', adminService);
/* #ngInject */
function adminService($http, environmentConfig) {
var service = {
getAllUsers: getAllUsers
};
return service;
/**
* Gets all user objects.
*/
function getAllUsers() {
return $http.get(environmentConfig.apiBaseUrl + '/user');
}
}
})();
and my unit tests look like this:
describe('AdminController', function() {
var ctrl,
adminService,
$scope;
var listOfTestUsers = [
{ name: 'Dave', id: 1 },
{ name: 'Bob', id: 2 },
{ name: 'Bill', id:3 }
];
beforeEach(function() {
module('blah');
});
beforeEach(inject(function($rootScope, $controller) {
adminService = {
getAllUsers: function() {}
};
spyOn(adminService, 'getAllUsers').and.returnValue(listOfTestUsers);
$scope = $rootScope.$new();
ctrl = $controller('AdminController', {
$scope: $scope,
adminService: adminService
});
}));
describe('The getAllUsers function should exist', function() {
it('should work', function() {
expect(ctrl).toBeDefined();
});
});
});
I get this error when running my Jasmine tests with Karma:
TypeError: adminService.getAllUsers(...).then is not a function
Here are a few things that I found wrong with the code.
.catch was used earlier. .then is called with two callbacks, a success callback and an error callback. So that's what I've done in your call to adminService.getAllUsers.
For the TypeError: adminService.getAllUsers(...).then is not a function that you were getting. You didn't mock getAllUsers properly. I've done that in the testCases file. It returns a function named then which was not available earlier.
Controller
(function() {
angular
.module('blah', [])
.controller('AdminController', AdminController);
/* #ngInject */
function AdminController($scope, toastr, adminService) {
$scope.greeting = "Hello World!";
/**
* Gets all users.
*/
$scope.getAllUsers = function() {
adminService.getAllUsers()
.then(function(response) {
$scope.users = response.data;
}, function(error) {
toastr.error('Unable to load users', 'Error');
console.log(error);
});
}
activate();
/**
* Controller initialisation.
*/
function activate() {
$scope.getAllUsers();
}
}
})();
environmentConfig Constant. Replace this with yours.
(function() {
angular.module('blah').constant('environmentConfig', {
apiBaseUrl: 'https://www.something.com'
})
})();
toastr Service. Replace this with yours
(function() {
angular
.module('blah')
.factory('toastr', toastr);
/* #ngInject */
function toastr() {
var service = {
error: error
};
return service;
/**
* Gets all user objects.
*/
function error(a, b) {
console.log("Here's the error : ", a);
}
}
})();
adminService
(function() {
angular
.module('blah')
.factory('adminService', adminService);
/* #ngInject */
function adminService($http, environmentConfig) {
/**
* Gets all user objects.
*/
function getAllUsers() {
return $http.get(environmentConfig.apiBaseUrl + '/user');
}
var service = {
getAllUsers: getAllUsers
};
return service;
}
})();
Test Cases
describe('controller: AdminController', function() {
var scope, $scope, toastr, adminService, AdminController, flag, $q;
flag = 'success';
var listOfTestUsers = [{
name: 'Dave',
id: 1
}, {
name: 'Bob',
id: 2
}, {
name: 'Bill',
id: 3
}];
beforeEach(module('blah'));
beforeEach(inject(function($controller, $rootScope, _toastr_, _adminService_, _$q_) {
scope = $rootScope.$new();
toastr = _toastr_;
adminService = _adminService_;
$q = _$q_;
spyOn(adminService, 'getAllUsers').and.callFake(function() {
return flag === 'success' ? $q.when(listOfTestUsers) : $q.reject("Error");
});
AdminController = $controller('AdminController', {
$scope: scope,
toastr: _toastr_,
adminService: _adminService_
});
}));
describe('The getAllUsers function should exist', function() {
it('should work', function() {
expect(AdminController).toBeDefined();
});
});
});
Hope this helps.
Your controller code is causing the error. You should be callling $scope.getAllUsers(); in your activate function, not "getAllUsers()".
Your actual error is the fact that your mock service does not have the function 'getAllUsers' here is a paste bin with the adjustments http://pastebin.com/LwG0CzUW
If you prefer you could adjust your test to call the actual service as the following pastebin.
http://pastebin.com/RSP4RfF9
My Angular 1.3 application is using the angular-translate library. In my Karma tests I'm attempting to mock the $translate provider with a Mock object I have created.
The mock object is called MockTranslate and it belongs to the myMocks module. I'm not including the source for MockTranslate in the question as it's not relevant to the question.
The subject of my test is a controller and I can quite easily mock $translate using the following:
module('myMocks');
inject(function($controller, MockTranslate) {
$controller("MyController", {
$translate: MockTranslate.create(translations);
});
});
The above mocking works, however my preference would be to mock the provider using the angular.mock.module with something like:
module('myMocks');
module("myModule", function($provide) {
$provide.provider("$translate", function(MockTranslate) {
return MockTranslate.create(translations);
});
});
But I get the following error when I run my tests:
Error: [$injector:modulerr] Failed to instantiate module function ($provide) due to: Error: [$injector:unpr] Unknown provider: MockTranslate
How do I mock a provider using angular.mock.module?
If I understood the task correctly then here is a working example:
angular.module('translateApp', [])
.controller('translateCtrl', function ($scope, $translate) {
$scope.translate = function(message) {
return $translate.translate(message);
};
})
.provider({
$translate: function() {
this.$get = function () {
return {
translate: function (msg) {
return 'OriginalTranslate: ' + msg;
}
};
};
}
});
describe('Translate Controller Test', function() {
var mockScope;
var mockTranslate;
beforeEach(module('translateApp', function($provide) {
$provide.provider('MockTranslate', function() {
this.$get = function () {
return {
translate: function (msg) {
return 'MockTranslate: ' + msg;
}
};
}
});
$provide.provider('$translate', function() {
this.$get = function (MockTranslate) {
return {
translate: function (msg) {
return MockTranslate.translate(msg);
}
};
}
});
}));
beforeEach(inject(function($controller, $rootScope, $translate) {
mockScope = $rootScope.$new();
mockTranslate = $translate;
$controller('translateCtrl', {
$scope: mockScope,
$translate: mockTranslate
});
}));
it('Translates messages', function () {
expect(mockScope.translate('cool message')).toEqual('MockTranslate: cool message');
});
});
We're unit testing our services and facing issue spying on methods with arguments of dependent services.
I am writing unit tests for ServiceA
ServiceA.js
angular.module("App").service("ServiceA", function($http, ServiceB) {
this.detail = null;
this.method = function(id){
var sevrB = new ServiceB();
return sevrB.getId(1).then(function(response) {
this.detail = response.data;
});
};
});
ServiceB.js (is a factory)
(function () {
var dependencies = [
'../module'
];
define(dependencies, function (module) {
return module.factory('ServiceB', function ($http) {
var ServiceB= function () {
this.id = null;
};
ServiceB.prototype.getId = function(Id) {
return $http.get('/test/');
}
}
}());
Unit test code
describe('Testing ServiceA', function () {
var serviceA, serviceBMock;
beforeEach(function () {
var _serviceBMock = function () {
return {
getId:function(id){
return 'test';
}
};
};
angular.module('ServiceAMocks', [])
.value('ServiceB', _serviceBMock);
});
beforeEach(module('ServiceAMocks'));
beforeEach(inject(function (_ServiceA_, _ServiceB_) {
serviceA=_ServiceA_;
serviceBMock=_ServiceB_;
});
it('retrive Id', function () {
spyOn(serviceBMock,'getId').and.Return('test');
serviceA.method(1);
});
});
I am spying on getId method of ServiceB from ServiceA and if i mocked ServiceB as function i am getting error below
Error: getId() method does not exist
at jasmineInterface.spyOn
If I mock serviceB as object then i get error as
TypeError: object is not a function
var _serviceBMock = {
getId:function(id){
return 'test';
}
}
And I am not sure of testing promise in this scenario.
This version supports Jasmine 1.3
I’m injecting $q service as ServiceB wants to call method then. We can even go forward and resolve returned promise, but this is next step in testing.
Answer to previous version of question, where AngularJS injects instance of serviceB
describe('ServiceA', function () {
var serviceA, ServiceB, $q;
beforeEach(function () {
module('App');
});
beforeEach(function () {
module(function ($provide) {
$provide.value('ServiceB', {
getId: jasmine.createSpy('ServiceB.getId').andCallFake(function () {
return $q.all();
})
});
});
});
beforeEach(inject(function (_ServiceA_, _ServiceB_, _$q_) {
$q = _$q_;
serviceA = _ServiceA_;
ServiceB = _ServiceB_;
}));
describe('.method()', function () {
it('returns ServiceB.getId() argument', function () {
serviceA.method(1);
expect(ServiceB.getId).toHaveBeenCalledWith(1);
});
});
});
jsfiddle: http://jsfiddle.net/krzysztof_safjanowski/sDh35/