jasmine test $scope is undefinded - angularjs

I try to write a controller test using karma with jasmine.
I get this error "Error: [$injector:unpr] Unknown SettingsProvider <- settings"
I been stuck for hours googling around but I can't found a solution for this.
My test Case
describe('MyController', function() {
var $scope, controller;
beforeEach(module('MyApp'));
beforeEach(inject(function ($rootScope, $controller) {
$scope = $rootScope.$new();
controller = $controller('MyController', {
$scope: $scope
});
}));
it('sets the options to "valid" if the type is val', function() {
var type = 'val';
$scope.callOptions(type);
expect($scope.options).toBeTruthy();
});
});
My karma.config.js
files: [
'app/bower_components/angular/angular.js',
'app/bower_components/jquery/dist/jquery.min.js',
'app/node_modules/angular-mocks/angular-mocks.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-ui-router/release/angular-ui-router.js',
'app/bower_components/angular-ui-router/release/angular-ui-router.min.js',
'app/metronic/assets/global/plugins/angularjs/plugins/angular-ui-router.min.js',
'app/bower_components/ui-router-extras/release/modular/ct-ui-router-extras.sticky.js',
'app/bower_components/ngDraggable/ngDraggable.js',
'app/metronic/assets/global/plugins/angularjs/angular-sanitize.min.js',
'app/metronic/assets/global/plugins/jquery.min.js',
'app/metronic/assets/global/plugins/bootstrap/js/bootstrap.min.js',
'app/metronic/assets/global/plugins/angularjs/plugins/ui-bootstrap-tpls.min.js',
'app/bower_components/ngstorage/ngStorage.min.js',
'app/bower_components/oclazyload/dist/ocLazyLoad.min.js',
'app/metronic/assets/global/plugins/angularjs/plugins/angular-file-upload/angular-file-upload.min.js',
'app/js/services/myProvider.js',
'app/js/app.js',
'app/controllers/MyController.js'
]
My controller :
MetronicApp.controller('MyController',
['$http',
'$rootScope',
'$scope',
'$window',
function ($http, $rootScope, $scope, $window) {
$scope.callOptions = function (type) {
if (type == 'val') {
return $scope.optionsVal;
}
;
if (type == 'Num') {
return $scope.optionsNum;
}
;
};
});
EDIT
I delete some file from my karma.config.js and now I get this error $scope is undefined ..This is a screen shot of the error :

I think the way you injected $rootScope and $controller is incorrect.Please try following code snippet.It will do the thing.
describe('MyController', function() {
var $scope, controller;
beforeEach(module('MyApp'));
beforeEach(inject(function ($injector) {
$scope = $injector.get("$rootScope").$new();
controller = $injector.get("$controller")('MyController', {
$scope: $scope
});
}));
it('sets the options to "valid" if the type is val', function() {
var type = 'val';
$scope.callOptions(type);
expect($scope.options).toBeTruthy();
});
});

Related

TypeError: undefined is not a constructor (evaluating 'angular.controller('myView')')

I'm getting the below error while doing karma/jasmine unit testing for both the test cases.I tried by modifying the controller by adding angular.controller in the spec file even then it is not working.Is there any way to fix?
TypeError: undefined is not a constructor (evaluating 'angular.controller('myView')')
myView.spec.js
// myView.spec.js
(function(){
describe('controller: myView', function(){
var module,myView,$q, $rootScope, $scope, uiGridConstants, overviewService, commonService, $timeout;
beforeEach(function() {
module = angular.module('app.myView');
controller= angular.controller('myView')
});
beforeEach(inject(function ($controller, _$q_, _$rootScope_, _$timeout_) {
$q= _$q_;
$rootScope = _$rootScope_;
$timeout= _$timeout_;
myView= $controller('myView', {
$q : _$q_,
$rootScope : _$rootScope_,
$timeout: _$timeout_
});
}));
describe("myViewto be defined", function() {
it("should be created successfully", function () {
expect(controller).toBeDefined();
});
it("overview should be defined", function () {
expect(myView()).toBeDefined();
});
});
});
})();
and myView.js
(function() {
'use strict';
angular
.module('app.myView')
.controller('myView', myView);
function myView($q, $rootScope, $scope, uiGridConstants, myViewService, commonService, $timeout) {
var vm = this;
vm.callFeedback = function () { };
})();
Sharing following code
// myView.spec.js
(function(){
describe('myView', function(){
var $controller, myView;
//we use angular-mocks to specify which modules we'll need within this
//test file.
beforeEach(angular.mock.module('app.myView'));
// Inject the $controller service to create instances of the controller
//(myView) we want to test
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
myView = $controller('myView', {});
}));
// Verify our controller exists
it('should be defined', function() {
expect(myView).toBeDefined();
});
});
})();
We set _$controller_to the $controller variable we created and then create an instance of our controller by calling $controller('myView', {}). The first argument is the name of the controller we want to test and the second argument is an object of the dependencies for our controller.
You should pass the injected parameters to your controller as shown:
(function() {
'use strict';
angular
.module('app.myView')
.controller($q,$rootScope,$scope,uiGridConstants,'myView', myView);
function myView($q, $rootScope, $scope, uiGridConstants, myViewService, commonService, $timeout) {
var vm = this;
vm.callFeedback = function () { };
})();
Also make sure that your module has all the necesary dependences in the angular.module('app.myView',['uiGridConstants', ...'etc']);

Unknown provider: $scopeProvider <- $scope

I am trying to make a small test work that validates wether the controller is defined.
The error I am receiving is:
myApp.orders module Order controller should .... FAILED
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <- OrdersCtrl
Reading similar errors it has something to do with the dependencies, but I don't know what's wrong.
Controller:
'use strict';
angular.module('myApp.orders', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/orders', {
templateUrl: 'orders/orders.template.html',
controller: 'OrdersCtrl'
});
}])
.controller('OrdersCtrl', function($scope, $location) {
$scope.changeView = function(view){
$location.path(view); // path not hash
}
});
Test:
'use strict';
describe('myApp.orders module', function() {
beforeEach(module('myApp.orders'));
describe('Order controller', function(){
it('should ....', inject(function($controller) {
//spec body
var OrdersCtrl = $controller('OrdersCtrl');
expect(OrdersCtrl).toBeDefined();
}));
});
});
This is because you are not passing the $scope variabl einside the controller when you are creating it in test. And the controller tries to define $scope.changeView, but it finds $scope as undefined.
You need to pass a $scope variable to the controller in your test.
var $rootScope, $scope, $controller;
beforeEach(function() {
module('myApp.orders');
inject(function (_$rootScope_, _$controller_) {
$rootScope = _$rootScope_;
$scope = _$rootScope_.$new();
$controller = _$controller_;
});
});
and in your test,
var OrdersCtrl = $controller('OrdersCtrl', { $scope: $scope });
Restructure your unit test slightly. We have a pattern where the controller is defined in the beforeEach() so it's ready for the test. You also need to import the controller you're testing:
import ControllerToTest from 'path/to/your/real/controller';
describe('myApp.orders module',() => {
let vm;
beforeEach(() => {
inject(($controller, $rootScope) => {
vm = $controller(ControllerToTest,
{
$scope: $rootScope.$new()
};
});
});
describe('Order Controller', () => {
it('should do something', () => {
expect(vm).toBeDefined();
});
});
});
Chaneg your controller like this
.controller('OrdersCtrl',['$scope', '$location', function($scope, $location) {
$scope.changeView = function(view){
$location.path(view); // path not hash
}
}]);

Angular controller not injected in test

I have a simple login controller:
'use strict';
angular.module('login', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
}])
.controller('LoginCtrl', ["$scope", "$route", "LoginService", function ($scope, $route, LoginService) {
var self = this;
this.showGuestLogin = true;
this.showUserLogin = false;
this.toggleUserLoginType = function () {
this.showGuestLogin = !this.showGuestLogin;
this.showUserLogin = !this.showUserLogin;
}
this.submitGuestLogin = function()
{
if(this.guestName === undefined || this.guestName.trim() == '')
{
self.loginError = "Name cannot be blank";
return;
}
LoginService.loginAsGuest(this.guestName.trim())
.then(function()
{
self.loginError = null;
$route.reload();
})
.catch(function(err)
{
self.loginError = 'An error occured. Please try again';
});
}
}]);
I am trying to test it with:
describe('LoginCtrl', function()
{
beforeEach(module('login'));
var ctrl;
beforeEach(inject(function($controller)
{
ctrl = $controller('LoginCtrl');
}));
it('should set error if guest name is undefined', function(done)
{
ctrl.guestName = undefined;
ctrl.submitGuestLogin();
expect(ctrl.loginError).toBeDefined();
});
});
But I am getting this error in console when test runs
Error: [$injector:unpr]
http://errors.angularjs.org/1.5.8/$injector/unpr?p0=%24scopeProvider%20%3C-%20%24scope%20%3C-%20LoginCtrl
I can see in the developer console in the karma driven browser that the controller and it's dependant files are all being loaded correctly.
I can't see what is wrong?
UPDATE
I have tried the suggestions of passing an empty object:
beforeEach(inject(function($controller, $scope, $route, LoginService)
{
ctrl = $controller('LoginCtrl', {
});
}));
and setting up the dependencies:
beforeEach(inject(function($controller, $scope, $route, LoginService)
{
ctrl = $controller('LoginCtrl', {
$scope: $scope,
$route: $route,
LoginService: LoginService
});
}));
Both of which give me this error:
Error: [$injector:unpr]
http://errors.angularjs.org/1.5.8/$injector/unpr?p0=%24scopeProvider%20%3C-%20%24scope
It's because you need to add in the scope in the injection like this:
beforeEach(inject(function($controller, $scope) {
ctrl = $controller('LoginCtrl', { $scope: $scope });
}));
Similarly, if your real controller has injections that you will be using for testing, you'll need to add them in. So for example (and this is only an example):
ctrl = $controller('LoginCtrl',
{
$scope: $scope,
SomeService: SomeService,
moment: moment,
dateFormat: dateFormat
});
Found an answer here which worked: Angular Unit Test Unknown provider: $scopeProvider
beforeEach(inject(function($controller, $rootScope, $route, LoginService)
{
scope = $rootScope.$new();
ctrl = $controller('LoginCtrl', {
$scope: scope
});
}));
In my case I didn't actually need $scope injected into my controller, so I removed it an the original code now works:
beforeEach(inject(function($controller, $rootScope, $route, LoginService)
{
ctrl = $controller('LoginCtrl');
}));
I need to read up on how mocks and injection works!

How to inject a service to jasmine

I have the following test case MeetingCtrlSpec.js
describe('ViewMeetingCtrl', function () {
var $rootScope, scope, $controller ;
beforeEach(angular.mock.module('MyApp'));
beforeEach(inject(function ($rootScope, $controller ) {
scope = $rootScope.$new();
$controller('ViewMeetingCtrl', {
$scope: scope,
});
}));
it('should change greeting value if name value is changed', function () {
//some assertion
});
});
ViewMeetingCtrl.js
(function () {
'use strict';
angular.module('MyApp').controller('ViewMeetingCtrl', ViewMeetingCtrl);
ViewMeetingCtrl.$inject = ['$scope', '$state', '$http', '$translate', 'notificationService', 'meetingService', '$modal', 'meeting', 'attachmentService'];
function ViewMeetingCtrl($scope, $state, $http, $translate, notificationService, meetingService, $modal, meeting, attachmentService) {
$scope.meeting = meeting;
//more code goes here
}
})();
this meeting comes from the app.routes.js file
.state('company.meeting', {
abstract: true,
url: '/meetings/:meetingId',
template: '<ui-view/>',
resolve: {
meeting: function(meetingService, $stateParams){
return meetingService
.getMeeting($stateParams.meetingId)
.then(function(response){
return response.data;
});
}
},
})
My problem is regarding the injection of meeting in this ctrl . I am not sure how to do inject that in my test case. I did like the following .
describe('ViewMeetingCtrl', function () {
var $rootScope, scope, $controller , meeting ;
beforeEach(angular.mock.module('MyApp'));
beforeEach(inject(function ($rootScope, $controller , meeting ) {
scope = $rootScope.$new();
$controller('ViewMeetingCtrl', {
$scope: scope,
meeting : meeting
});
}));
it('should change greeting value if name value is changed', function () {
//some assertion
});
});
... and i got this error Error: [$injector:unpr] Unknown provider: meetingProvider <- meeting
How do i inject meeting dependency to my test case . ?
Meeting is not a service, but an object that is injected when route is resolve. In you test case you should explicitly create the meeting dummy object.
beforeEach(inject(function ($rootScope, $controller,$q ) {
scope = $rootScope.$new();
$controller('ViewMeetingCtrl', {
$scope: scope,
meeting : {} //your custom object
});
}));
Remember you are testing the controller in your test not the route resolution injection.

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);
});
});
});

Resources