Ionic unit test in controller, spyOn not passing - javascript

It's a very simple test.. and it's not passing.. If someone can throw some light into this :)
This is the controller code (part of it) that needs to be tested
AppCtrl
$scope.requestAuthorization = function() { requestAuthorization(); };
if ($stateParams.requestAuthorization === true) {
console.log('$stateParams.requestAuthorization');
$scope.requestAuthorization();
}
function requestAuthorization() {
console.log('requestAuthorization()');
// more code here..
}
Test
describe('AppCtrl', function() {
var AppCtrl, $rootScope, $scope, $stateParams;
beforeEach(module('myapp'));
// disable ionic cache to avoid GET errors
beforeEach(module(function($provide, $urlRouterProvider) {
$provide.value('$ionicTemplateCache', function() {});
$urlRouterProvider.deferIntercept();
}));
beforeEach(inject(function($controller, _$rootScope_, _$injector_, _$stateParams_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$stateParams = _$stateParams_;
AppCtrl = $controller('AppCtrl',{
$scope: $scope
});
spyOn($scope, 'requestAuthorization');
$stateParams.requestAuthorization = true;
}));
it('$stateParams.requestAuthorization should be defined', function() {
expect($stateParams.requestAuthorization).toBeDefined();
});
it('$scope.requestAuthorization should be defined', function() {
expect($scope.requestAuthorization).toBeDefined();
});
// this test is not passing..
it('should call requestAuthorization', function() {
expect($scope.requestAuthorization).toHaveBeenCalled();
});
});
The function is actually being called, I can see the console.log in the console, but it's not passing.
Easy tests, all passing.. except the last one..
Thanks for your time :)
NOTE: There is a $stateParams.requestAuthorization, and a $scope.requestAuthorization. First one is boolean, the other a function, the function is not passing.

In your beforeEach block, define the $stateParams before instanciate the Controller.
beforeEach(inject(function($controller, _$rootScope_, _$injector_, _$stateParams_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$stateParams = _$stateParams_;
$stateParams.requestAuthorization = true;
AppCtrl = $controller('AppCtrl',{
$scope: $scope,
$stateParams: $stateParams
});
spyOn($scope, 'requestAuthorization');
}));

Related

Getting method as undefined while running karma test

I have created a scope method inside my controller which is executing when a button is pressed. I am writing unit test cases for the same. I have injected my module in beforeEach block and created spyon my scope function and then using it in 'it' method and checking whether it is called or not. But getting an error as a method not found.
Controller
'use strict';
angular.module('myApp.view1', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}])
.controller('View1Ctrl', ['$scope',View1Ctrl])
function View1Ctrl($scope) {
$scope.user = {
name: '',
last: ''
}
$scope.showFormData = function() {
$scope.formData = $scope.user.name + $scope.user.last;
}
}
spec.js
'use strict';
describe('myApp.view1 module', function () {
var $controller, $rootScope;
beforeEach(module('myApp.view1'));
beforeEach(inject(function (_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('view1 controller', function () {
var $scope, controller, formData;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('View1Ctrl', {
$scope: $scope
});
spyOn(controller, 'showFormData');
});
it('should check for the show form details', function () {
$scope.user.name = "Sandeep";
$scope.user.last = "Gupta";
expect($scope.showFormData).toHaveBeenCalled();
expect($scope.user.name + $scope.user.last).toEqual(firstname);
});
});
});
Need help to resolve this issue.
It looks like you're trying to spy on the showFormData method of the controller:
spyOn(controller, 'showFormData');
However, the showFormData doesn't exist on the controller, it's a method of the controller's scope.
You'll need to use:
spyOn($scope, 'showFormData');
It's also important to know that you need to use the same object to both spyOn and expect(...).toHaveBeenCalled(). In your case you where spying on controller.showFormData(), yet expecting $scope.showFormData() to have been called.

Angular directive controller unit test using window.confirm

I am trying to get 100% test coverage for a directive. The directive has a controller with a function that uses the window.confirm method.
'use strict';
(function() {
angular
.module('app')
.directive('buttonToggle', buttonToggle);
function buttonToggle() {
var buttonToggleController = ['$scope', function($scope) {
$scope.toggle = function() {
var confirmResponse = (window.confirm('Are you sure?') === true);
if(confirmResponse) {
$scope.on = !$scope.on;
}
return $scope.on;
};
}];
return {
restrict: 'E',
templateUrl: 'client/modules/buttonToggle/buttonToggle.html',
replace: true,
scope: {
on: '='
},
controller: buttonToggleController
};
}
})();
I have tested to make sure that everything is defined, but I am not able to enter the if statement in the controller's $scope.toggle method.
describe('The buttonToggle directive', function() {
var $compile,
$scope,
btElement = '<button-toggle></button-toggle>',
compiledElement,
window,
confirm,
btElementPath = 'client/modules/buttonToggle/buttonToggle.html',
btController;
beforeEach(module('app'));
beforeEach(module(btElementPath));
beforeEach(inject(function(_$compile_, _$rootScope_, $templateCache, $window) {
$compile = _$compile_;
window = $window;
spyOn(window, 'confirm');
$scope = _$rootScope_.$new();
var template = $templateCache.get(btElementPath);
$templateCache.put(btElementPath, template);
var element = angular.element(btElement);
compiledElement = $compile(element)($scope);
$scope.$digest();
btController = element.controller('buttonToggle', {
$window: window
});
scope = element.isolateScope() || element.scope();
}));
it('should be defined', function() {
expect(compiledElement.html()).toContain('btn');
});
describe('buttonToggle controller', function() {
it('should be defined', function() {
expect(btController).not.toBeNull();
expect(btController).toBeDefined();
});
describe('toggle', function() {
it('should be defined', function() {
expect(scope.toggle).toBeDefined();
});
it('should confirm the confirmation dialog', function() {
scope.toggle();
expect(window.confirm).toHaveBeenCalled();
});
});
});
});
I am guessing it has something to do with mocking the $window service, but I'm not sure if I will be able to test that since it isn't declared globally. So, is the controller's function fully "unit testable"? If not, should I write the directive's controller in a separate file and use angular.module.controller? If yes, then how am I able to test it, or what am I missing?
Use angular's $window service instead of window directly, which is what you are doing in your test but not in your directive.
Then you can mock any of its functions:
spyOn($window, 'confirm').and.returnValue(false);

Angular Jasmine UI router inject resolve value into test

In my Angular app, UI router resolves a promise into the controller. When trying to test this controller, Karma is complaining about an unknown provider. How do I inject a fake object into the test to represent this resolve object.
My app's code looks something like:
angular.module('myapp')
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tab.name', {
...
resolve: {
allTemplates: function(Templates) {
return Templates.all().then(function(templates) {
return templates;
});
}
}
})
})
.controller('QueriesCtrl', function(allTemplates, UserQuery) {
var vm = this;
vm.queries = allTemplates;
vm.goToUrl = function(index, data) {
var processedUrl = UserQuery.process(data, vm.queryTyped[index]);
UserQuery.goToUrl(processedUrl);
};
});
When trying to run tests I get the error
Unknown provider: allTemplatesProvider <- allTemplates <- QueriesCtrl
I've tried creating a spy and injecting it, but this does not work. Here's my test at the moment:
describe('Unit: queriesCtrl', function() {
var controller,
scope,
UserQuery;
beforeEach(function() {
module('myapp');
inject(function($injector) {
UserQuery = $injector.get('UserQuery');
allTemplates = jasmine.createSpyObj('allTemplates', [{a:1}, {a:2}, {b:3}]);
});
});
describe('goToUrl', function() {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('QueriesCtrl as ctrl', {
'$scope': scope
});
}));
it('should call UserQuery.process()', function() {
spyOn(UserQuery, 'process');
scope.ctrl.goToUrl();
expect(UserQuery.process).toHaveBeenCalled();
});
});
});
Since there is no route involved in unit test you would have to inject the allTemplates as a normal object with $controller function. Can you try:
controller = $controller('QueriesCtrl as ctrl', {
'$scope': scope,
'allTemplates':allTemplates
});
Else you can use the $provide API to create a dummy service.
module(function ($provide) {
$provide.value("allTemplates", {[{a:1}, {a:2}, {b:3}]});
Do it first thing in your beforEach block.

Mocking $state change in AngularJS controller

Main module being injected with everything;
require('./dashboard');
module.exports = angular.module('college', ['college.dashboard'])
.config(function ($stateProvider) {
$stateProvider
.state('college.list', {
url: '/college',
templateUrl: '/dashboard/dashboard.html',
controller: 'DashboardCtrl',
authenticate: true
});
})
.factory('ProjectFactory', require('./services/college.service'));
College Index, which makes the dashboard controller available;
module.exports = angular.module('college.dashboard',
[])
.controller('DashboardCtrl', require('./dashboard.controller.js'));
The college controller exposes the following method;
module.exports = function($scope, $rootScope, $state) {
$scope.openCollege = function(id) {
$rootScope.currentCollege = id;
$state.go('college.main', {currentCollege: id});
};
};
The following error is thrown when the unit test calls
scope.openCollege (2);
Error:
Error: Could not resolve 'college.main' from state ''
Creating state;
beforeEach(inject(function ($rootScope, $state, $location, $controller) {
scope = $rootScope.$new();
location = $location;
rootScope = $rootScope;
$rootScope.currentCollege = {};// Empty by default
state = $state;
$controller('DashboardCtrl', {
$scope: scope,
$state: state,
$location: location
});
}));
Some of the spec test code;
expect(state.current.name).to.equal('');
scope.openCollege(2);
I need to figure out how to handle/mock $state.go during the Karma unit testing so that state knows about college.main.
Any help is appreciated.
J
Here is how I got it working;
I added the following to the spec test;
// Globally defined
var stateSpy;
// within the beforeEach
stateSpy = sinon.stub($state, 'go');
// In the unit test
scope.openCollege (2);
assert(stateSpy.withArgs('college.main', '{currentCollege: 2}').calledOnce);
Note: the $state was not passed to the the controller.
I now have green tests!
Thanks for your help, gave me the idea of how to make this work.
J
You should use
it('should be able to go to person edit state', function () {
DashboardCtrl();
scope.openProject('12345');
scope.$digest();
expect(state.go).toHaveBeenCalledWith('college.main', { id : '12345' });
});

How to test scope values after service call http.get

I am trying to write a unit test to test that tests a factory that performs a http.get and then tests the scope bindings.
The factory is called within my controller.
Here's a plunker showing my http.get: http://plnkr.co/edit/VqUSeTiEj3MP37tAXKad?p=preview
Ctrl:
app.controller('MainCtrl', function($scope, $http, factoryGetJSONFile) {
$scope.name = 'World';
factoryGetJSONFile.getMyData(function(data) {
$scope.Addresses = data.Addresses.AddressList;
$scope.People = data.Names.People;
$scope.Country = data.Country;
});
});
Test:
describe('with httpBackend', function () {
var app;
beforeEach(function () {
app = angular.mock.module('plunker')
});
describe('MyCtrl', function () {
var scope, ctrl, theService, httpMock;
beforeEach(inject(function ($controller, $rootScope, factoryGetJSONFile, $httpBackend) {
httpMock = $httpBackend;
scope = $rootScope.$new();
ctrl = $controller('MyCtrl', {
$scope: scope,
factoryGetJSONFile: factoryGetJSONFile,
$httpBackend: httpMock
});
}));
it("should make a GET call to data.json", function () {
console.log("********** SERVICE ***********");
httpMock.expectGET("data.json?").respond(data);
console.log(data.Addresses);
console.log(data.Names);
console.log(data.Country);
//expect(factoryGetJSONFile.getMyData()).toBeDefined();
httpMock.flush();
});
})
});
The test for the http.get seems ok, but when i try logging the reponse (data), an error occurs.
UPDATE:
When i try to log the call via:
console.log(httpMock.expectGET("data.json?").respond(data));
Undefined is displayed.

Categories

Resources