Ionic Framework - Unit testing controller using $ionicView.enter - angularjs

I'm trying to unit test a controller that uses $ionicView.enter like this
myControllers.controller('MyCtrl', [
'$scope',
function($scope) {
'use strict';
$scope.$on('$ionicView.enter', function(){
$scope.myValue = 1;
});
}]);
In my unit test I can spy on the event and check it's been called, but it doesn't then get into the function and set $scope.myValue. This is my test:
it('should do stuff', function () {
spyOn(scope, '$on').and.callThrough();
scope.$on('$ionicView.enter');
// this passes
expect(scope.$on).toHaveBeenCalledWith('$ionicView.enter')
// this fails - scope.myValue is undefined
expect(scope.myValue).toEqual(1);
});
I thought callThrough would call the code inside the function I'm spying on, but doesn't seem to here.
Any help appreciated.

The answer was to trigger the ionicView enter event after my controller set up in the test rather than try to spy on it.
beforeEach(inject(function($controller) {
scope = $rootScope.$new();
$controller('MyCtrl', {
$scope: scope
});
scope.$emit('$ionicView.enter');
scope.$digest();
}));

Related

AngularJS testing rootScope.$broadcast not working

My controller method looks like this:
angular.module(_appName_)
.controller('myController', function ($scope, $rootScope) {
$rootScope.$broadcast('myObj', false);
......some code here.......
});
Jasmine test for testing call made to $rootScope.$broadcast looks like this:
describe("myController",function(){
var scope,rootScope;
beforeEach(angular.mock.inject(function($rootScope) {
scope = $rootScope.$new();
rootScope = $rootScope;
}));
describe('myController', function() {
it('rootScope broadcast called for myObj with false value', inject(function($controller, $rootScope) {
var requestObj = '{"key":"1234567890"}';
rootScope.requestObject = requestObj;
$controller('myController', {
$scope: scope,
$rootScope: rootScope
});
spyOn($rootScope, '$broadcast').and.callThrough();
expect($rootScope.$broadcast).toHaveBeenCalled();
}));
});
});
It always gives me the following error:
Expected spy $broadcast to have been called.
at Object.
When i try to put a breakpoint on the line where there is a call to broadcast in the controller method, it does hit the breakpoint while debugging. So the actual call is being made but the test doesn't recognize it somehow.
Can someone please let me know what am I missing here ?
I think you forgot to include your module in beforeEach function.
And then make sure you mock your spyOn($rootScope, '$broadcast') before you initialize your controller
$controller('myController', {
$scope: scope,
$rootScope: rootScope
});
Here is a plunker. :)

AngularJS Jasmine Unit Testing - Controller method is never called

I'm having a hard time to get my head around Jasmine. The following test is failing with the message "Expected spy init to have been called. at Object.."
The Test
beforeEach(module('myModule'));
it('Should execute myCtrl.init() on controller instantiation', function () {
var $scope = $rootScope.$new();
$scope.foo = 'bar';
var MyCtrl = $controller('MyCtrl', {
$scope: $scope
});
spyOn($scope, 'init');
expect($scope.init).toHaveBeenCalled();
expect($scope.foo).toBe('bar');
});
The Controller
angular.module('myModule')
.controller('MyCtrl', [
'$scope'
function($scope) {
$scope.init = $scope.init || function init () {
$scope.foo = $scope.foo || 'baz';
};
$scope.init();
}]);
What am I missing?
Basically, you can't setup the spy after the controller is created, or it won't catch the method being called at controller invocation. But that is not something that really needs to be tested because you can tell based on the controller code that it is getting called when the controller is invoked, and the only thing that would keep it from being called is some kind of syntax error or something.
You are ok to test whether some $scope property like $scope.foo is set to the correct value, though.
This seems like a similar problem to AngularJs unit test - Check if "Init" function was called
Edit:
If you really needed to though, you could use a scope mock like this (This is what you should do to mock other dependencies you might have to your controller) and inject it instead of the $rootScope way, but then you wouldn't have $scope.foo getting set because $scope.init would not be initially undefined:
var scopeMock;
beforeEach(module('myModule'));
it('Should execute myCtrl.init() on controller instantiation', function () {
scopeMock = jasmine.createSpyObj('$scope', ['init']);
var MyCtrl = $controller('MyCtrl', {
$scope: scopeMock
});
//This will be true according to the controller you defined
expect(scopeMock.init).toHaveBeenCalled();
});

'myApp.controller is not a function' error in Jasmine

I'm getting the following error in the js console log when attempting to assign the controller to 'myCtrl' variable in a jasmine test: 'myApp.controller is not a function'
In the controller itself, the controller is defined as the following and this is what's triggering the error I mentioned:
myApp.controller('myCtrl', ...
And this is how I'm trying to access the controller in my spec file:
beforeEach(function() {
module('myApp');
});
it('tests the controller', inject(function($controller) {
var myCtrl = $controller('myCtrl');
}));
Any idea why it's still throwing this error? Seems to be a missing dependency but not sure where..
If you want to test your controller , following is one way of writing a test case
describe('YourControllerName', function () {
var $rootScope, scope, $controller ;
beforeEach(angular.mock.module('YourModuleName'));
beforeEach(inject(function ($rootScope, $controller ) {
scope = $rootScope.$new();
$controller('YourControllerName', {
$scope: scope
});
}));
it('Should do this', function () {
//assertions
});
});
Thanks - It turns out it was something as simple as I need to list myApp.js before myController.js in the SecRunner.html. I was pretty sure I tried that previously.. but there you go.

Jasmine test run with Karma cannot find Angular method called from onload

First off I am new to Karma, I have the following code in an application...
$(document).ready(function() {
angular.element('#eventListController').scope().initEvents();
});
but when I run karma I see....
Uncaught TypeError: Cannot read property 'initEvents' of undefined
What am I missing here? I am using a Sanity check test...
describe("Sanity test for jasmine", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
Update
So I think I am having this issue because there is no DOM right now. How do I mock out this DOM I guess would be the real question.
new version still failing...
describe("Sanity test for jasmine", function() {
beforeEach(module("Events"));
describe("TailsCtrl", function () {
var scope,
controller;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
}));
it("should assign message to hello world", function () {
expect(true).toBe(true);
});
});
});
Update
Looks like I need jasmine-jquery but it fails to install on my local windows GRRRRRR
What are you testing? A controller? A directive? You have to provide a real scope for your tests. Here is an example for a controller test in jasmine:
describe("controller", function() {
beforeEach(function() {
$scope = $rootScope.$new();
HomeCtrl = $controller('HomeCtrl', { $scope: $scope, $rootScope: $rootScope });
$rootScope.$digest();
});
it("foobar", function() {});
});
Now your controller has a real scope, mocked out for your tests.
Looks like I need a couple plugins and something like this will work
http://a-developer-life.blogspot.com/2011/06/jasmine-part-2-spies-and-mocks.html

AngularJS controller instantiation in unit tests - functions not binding to scope values

I have a service that synchronously returns data to a controller:
angular.module('app').controller(function($scope, myService) {
$scope.foo = myService.getFoo();
});
This works just fine in the browser. In my unit tests, $scope.foo is undefined:
beforeEach(function () {
module('app');
myService = jasmine.createSpyObj('myService', ['getFoo']);
inject(function($controller, $rootScope) {
$scope = $rootScope.$new();
ctrl = $controller('ModelSliderCtrl', {
myService: myService,
$scope: $scope
});
});
});
it('should have foo on the scope', function() {
myService.getFoo.and.returnValue({});
expect(myService.getFoo).toHaveBeenCalled(); // PASS
$scope.$digest();
expect($scope.foo).toBeDefined(); // FAIL - $scope.foo is undefined
});
This does work in both the browser and tests:
angular.module('app').controller(function($scope, myService) {
$scope.init = function() {
$scope.foo = myService.getFoo();
};
$scope.init();
});
.
it('should have foo on the scope', function() {
myService.getFoo.and.returnValue({});
$scope.init();
expect(myService.getFoo).toHaveBeenCalled(); // PASS
expect($scope.foo).toBeDefined(); // PASS
});
I'd like to believe I'm fairly well-versed in Angular, Jasmine and JavaScript. I've also asked some colleagues who are equally puzzled.
Can anyone explain to me what is going on here?
You are setting up a mock
it('should have foo on the scope', function() {
myService.getFoo.and.returnValue({});
after your controller has been instantiated. It's too late to set up the mock by then, do it before instantiating your controller since you are executing init() right away.
myService = jasmine.createSpyObj('myService', ['getFoo']);
myService.getFoo.and.returnValue({});
inject(function($controller, $rootScope) {

Resources