Angular, basic test not working - angularjs

Controller:
angular.module('ngApp', [
'templates-app',
'templates-common',
'ngApp.home',
'ui.router'
])
.config(function myAppConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
})
.run(function run() {})
.controller('AppCtrl', function AppCtrl($scope) {
$scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
if (angular.isDefined(toState.data.pageTitle)) {
$scope.pageTitle = toState.data.pageTitle + ' | App';
}
});
});
Test:
describe('AppCtrl', function() {
beforeEach(module('ngApp'));
var $controller;
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
}));
describe('$scope.pageTitle', function() {
it('should set pageTitle', inject(function() {
var $scope = {};
var AppCtrl = $controller('AppCtrl', { $scope: $scope });
expect(true).toBe(true);
}));
});
});
I am not testing anything right now, just loading the controller. However this fails
Chrome 41.0.2272 (Mac OS X 10.10.2) AppCtrl $scope.pageTitle should set pageTitle FAILED
TypeError: undefined is not a function
at new AppCtrl (/src/app/app.js:15:9)
at invoke (/vendor/angular/angular.js:4203:17)
at Object.instantiate (/vendor/angular/angular.js:4211:27)
at /vendor/angular/angular.js:8501:28
at /vendor/angular-mocks/angular-mocks.js:1878:12
at Object.<anonymous> (/src/app/app.spec.js:14:19)
at Object.invoke (/vendor/angular/angular.js:4203:17)
at Object.workFn (/vendor/angular-mocks/angular-mocks.js:2436:20)
Error: Declaration Location
at window.inject.angular.mock.inject (/vendor/angular-mocks/angular-mocks.js:2407:25)
at Suite.<anonymous> (/src/app/app.spec.js:12:30)
at Suite.<anonymous> (/src/app/app.spec.js:11:2)
at /src/app/app.spec.js:1:1
Any ideas whats wrong with my test setup?
Actual test:
describe('$scope.pageTitle', function() {
it('should be defiend after stateChange', inject(function() {
var $scope = {};
var AppCtrl = createController('AppCtrl');
scope.$broadcast('$stateChangeSuccess', {
data: {
pageTitle: 'bar'
}
});
expect(scope.pageTitle).toBeDefined();
}));
});

You need to create the scope from the $rootScope and inject it:
beforeEach(inject(function (_$controller_, $rootScope) {
$controller = _$controller_;
scope = $rootScope.$new();
createController = function (ctrlName) {
return $controller(ctrlName, {
'$scope': scope
});
};
}));
In this way you can use the createController function for the purpose and using:
var AppCtrl = createController('AppCtrl');
For having the controller.
Here is a working jsfiddle:
http://jsfiddle.net/Lbs6sk0k/2/
EDIT: where is $scope empty?
if I put a console.log here:
.controller('AppCtrl', function AppCtrl($scope) {
console.log($scope);
I can see this:
EDIT 2:
Set the the scope as a global variable:
describe('AppCtrl', function () {
beforeEach(module('ngApp'));
var $controller;
var scope;
Then, in you test case, you can use it:
it('should set pageTitle', inject(function () {
var $scope = scope;
Updated jsfiddle with your new test case: http://jsfiddle.net/Lbs6sk0k/3/

I think the problem is with the scope you're injecting into the controller and the $scope.$on... . Try this:
describe('AppCtrl', function() {
beforeEach(module('ngApp'));
var $controller, $scope;
beforeEach(inject(function(_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_.$new();
}));
describe('$scope.pageTitle', function() {
it('should set pageTitle', inject(function() {
var AppCtrl = $controller('AppCtrl', { $scope: $scope });
expect(true).toBe(true);
}));
});
});

Related

Mock Service Within Controller Returning Undefined

I am trying to test something pretty simple: a controller that calls a service that performs a http request.
Controller:
define(['module'], function (module) {
'use strict';
var MyController = function ($scope, MyService) {
$scope.testScope = 'karma is working!';
MyService.getData().then(function (data) {
$scope.result = data.hour
});
};
module.exports = ['$scope', 'MyService', MyController ];
});
Test:
define(['require', 'angular-mocks'], function (require) {
'use strict';
var angular = require('angular');
describe("<- MyController Spec ->", function () {
var controller, scope, myService, serviceResponse;
serviceResponse= {
id: 12345,
hour: '12'
};
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$controller_, _$rootScope_, _MyService_, $q) {
scope = _$rootScope_.$new();
var deferred = $q.defer();
deferred.resolve(serviceResponse);
myService = _MyService_;
spyOn(myService, 'getData').and.returnValue(deferred.promise);
controller = _$controller_('MyController', {$scope: scope});
scope.$apply();
}));
it('should verify that the controller exists ', function() {
expect(controller).toBeDefined();
});
it('should have testScope scope equaling *karma is working*', function() {
expect(scope.testScope ).toEqual('karma is working!');
});
});
});
With the above, i get the error:
TypeError: 'undefined' is not an object (evaluating 'spyOn(myService, 'getData').and.returnValue')
Solved the question - was using Jasmine 1x.. Upgraded to 2.0 and works as expected

TypeError: $controller is not a function - angularjs with jasmine

I am trying to set up a test for AngularJS app using Jasmine. It follows the docs but is a bit simpler. The fiddle has the following code:
angular.module('myapp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
$scope.greeting = "hello";
}]);
describe('My controller', function() {
var $controller;
module('myapp');
inject(function(_$controller_) {
$controller = _$controller_;
});
it('greets', function() {
var $scope = {};
var controller = $controller('MyCtrl', {
$scope: $scope
});
expect($scope.greeting).toEqual('hello');
})
});
And Jasmine reports an error: TypeError: $controller is not a function.
How to correct the code to get rid of this error and be able to test the controller?
You need to instantiate the app module and inject $controller for each test using beforeEach blocks:
describe('My controller', function() {
var $controller;
beforeEach(module('myapp'));
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
}));
it('greets', function() {
var $scope = {};
var controller = $controller('MyCtrl', {
$scope: $scope
});
expect($scope.greeting).toEqual('hello');
})
});
Demo: https://jsfiddle.net/f5ebb55f/6/

why the message property is undefined?

I make one controller in Angularjs and try to test that controller using Jasmine. I got this error Cannot read property 'message' of undefined why ?
Here is my code.
controller
(function(){
'use strict';
angular.module('app.home').controller('homeCntrl',homeCntrl);
function homeCntrl(){
var home=this;
home.clickbtn=function(){
home.message='test';
alert(home.message)
}
}
})();
Testing
(function(){
'use strict';
describe('http controller test', function() {
var $rootScope,
$scope,
controller,
$q,
$httpBackend;
beforeEach(function() {
module('app');
inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
controller = $injector.get('$controller')('homeCntrl', {
$scope: $scope
})
})
})
describe('Init value', function() {
it('check name value', function() {
expect(controller.message).toBeUndefined();
})
})
it('it should be true', function() {
expect(true).toBeTruthy();
})
})
})();
any update ?durning testing I got this error .. ? can we do testing of this controller ?Every thing is fine on angular js code problem is on test code..only check appspec.js
Just an hint
app
(function() {
'use strict';
function HomeController() {
var home = this;
home.title = 'Home';
}
angular.module('home.controllers', [])
.controller('HomeController', HomeController);
})();
test
'use strict';
describe('home controller', function() {
var $controller;
var scope;
beforeEach(module('home.controllers'));
beforeEach(inject(function(_$controller_, $rootScope) {
$controller = _$controller_;
scope = $rootScope.$new();
$controller('HomeController as home', {$scope: scope});
}));
it('should have text = "Home"', function() {
expect(scope.home.title).toEqual('Home');
});
});
in your case the test should be like
scope.home.clickbtn();
expect(scope.home.message).toEqual('test');
Take a look at http://www.bradoncode.com/tutorials/angularjs-unit-testing/ to master unit test in angular

AngularJS + Jasmine Noob: How to access $scope in spec?

This is my first time testing using Jasmine. I'm having trouble accessing the $scope variables in the spec. I have a failing test:
mysite ProductsDetailCtrl sets hey
Expected undefined to be 1.
Error: Expected undefined to be 1.
spec:
//= require helpers/load-angular-mysite-module
//= require products/controllers/products_detail_controller
describe('mysite', function() {
var $rootScope, $scope, $controller;
beforeEach(function() {
module('mysite');
});
describe('ProductsDetailCtrl', function() {
beforeEach(inject(function(_$rootScope_, _$controller_) {
$rootScope = _$rootScope_; // don't really
$scope = $rootScope.$new(); // understand what's
$controller = _$controller_; // going on in this function
controller = $controller('ProductsDetailCtrl', {
'$rootScope': $rootScope,
'$scope': $scope
});
}));
it('sets hey', function() {
expect($rootScope.hey).toBe(1);
});
});
});
controller:
app.controller('ProductsDetailCtrl', ['$scope', '$resource', function($scope, $resource) {
$scope.hey = 1;
....
Could someone explain to me how I would access the scope?
You just have to check for the property heyin your $scope not in the $rootScope:
describe('mysite', function() {
var scope, ProductsDetailCtrl;
beforeEach(function() {
module('mysite');
});
describe('ProductsDetailCtrl', function() {
beforeEach(inject(function($controller, $rootScope) {
// Create a mock scope for your controller.
scope = $rootScope.$new();
// Initialize the controller with the mocked scope.
ProductsDetailCtrl = $controller('ProductsDetailCtrl', {
$scope: scope
});
}));
it('sets hey', function() {
expect(scope.hey).toBe(1);
});
});
});

How can I test a $scope.$on event in a Jasmine test?

I am unit testing a controller and I want to test an event handler. Say my controller looks like:
myModule.controller('MasterController', ['$scope', function($scope){
$scope.$on('$locationChangeSuccess', function() {
$scope.success = true;
});
}]);
Would I broadcast that in my Jasmine test? Would I emit it? Is there an accepted standard?
The solution I came up with is as follows:
describe('MasterController', function() {
var $scope, $rootScope, controller, CreateTarget;
beforeEach(function() {
inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
var $controller = $injector.get('$controller');
CreateTarget = function() {
$controller('MasterController', {$scope: $scope});
}
});
});
describe('$locationChangeSuccess', function() {
it('should set $scope.success to true', function() {
controller = CreateTarget();
$rootScope.$broadcast('$locationChangeSuccess');
expect($scope.success).toBe(true);
});
});
});
I don't think there is "an accepted standard" but according to $location source code the event is broadcasted, so I would mock this behavior and test it this way:
'use strict';
describe('MasterController', function() {
var MasterController,
$rootScope,
$scope;
beforeEach(module('myModule'));
beforeEach(inject(function($rootScope, $injector, $controller) {
$rootScope = $rootScope;
$scope = $rootScope.$new();
MasterController = $controller('MasterController', {
'$scope': $scope
});
$scope.$digest();
}));
describe('$locationChangeSuccess event listener', function() {
it('should set $scope.success to true', function() {
var newUrl = 'http://foourl.com';
var oldUrl = 'http://barurl.com'
$scope.$apply(function() {
$rootScope.$broadcast('$locationChangeSuccess', newUrl, oldUrl);
});
expect($scope.success).toBe(true);
});
});
});

Resources