Test angular controllers with Jasmine - module issue - javascript

I decided to learn how to test my angular code with Jasmine. Everything works when I don't use specific dependencies but if there are some dependencies I have problems. For example we have controllers.js:
angular.module('myApp', ['jmdobry.angular-cache'])
.controller('MyCtrl', ['$scope', '$http', function($scope, $http) {
$scope.myName = "Wojtek";
/...
}]);
And now I want to test:
describe('myApp', function() {
var scope,
controller;
beforeEach(angular.mock.module('myApp'));
describe('MyCtrl', function() {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('sets proper name', function () {
expect(scope.myName).toBe("Wojtek");
});
});
});
My question is - how to mock that 'jmdobry.angular-cache' dependency?

Since you don't need the actual mocking functionality for that module in your tests you can do something like this:
describe('myApp', function () {
var scope,
controller;
beforeEach(angular.mock.module('jmdobry.angular-cache', [])); // just create a module that does nothing
beforeEach(angular.mock.module('myApp'));
describe('MyCtrl', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope
});
}));
it('sets proper name', function () {
expect(scope.myName).toBe("Wojtek");
});
});
});

Related

karma test controller using toBeDefined failed

My test failed because it says my controller is not defined. So strange I think I did everything right.
describe('homeCtrl', function() {
var httpBackend, controller, scope;
beforeEach(module('App'));
beforeEach(inject(function($httpBackend, $controller) {
scope = {};
httpBackend = $httpBackend;
controller = $controller('homeCtrl', { $scope: scope });
}));
it('should exist', function() {
expect(controller).toBeDefined();
});
});
and I have my home.js which is the controller like this
var App = angular.module('App')
App.controller('homeCtrl', function($scope) {
})
The error is Expected undefined to be defined.
Your home.js should have dependencies injected in module, change it as,
var App = angular.module('App',[])
App.controller('homeCtrl', function($scope) {
})

How to test $http with Jasmine in AngularJS

I'm trying to test the data received from an $http request in my controller.
I don't have too much experience testing with Angular so I'm struggling to under stand how to do it.
$scope. always comes back undefined and when I've tried fetching the data from the test, that seems to fail also. What am I missing?
Controller:
'use strict';
var myApp = angular.module('myApp.view1', ['ngRoute']);
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}]);
myApp.controller('View1Ctrl', [
'$scope',
'$http',
function($scope, $http) {
$http.get('view1/data.json')
.then(function(res){
$scope.data = res.data.data
});
}]);
Test:
'use strict';
describe('myApp.view1 module', function() {
beforeEach(module('myApp.view1'));
describe('view1 controller', function(){
var scope, testCont;
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
testCont = $controller('View1Ctrl', {$scope: scope});
}));
it('should....', function(){
expect($scope.data).toBeDefined();
});
});
});
The HTTP requests will not fire unless you call $httpBackend.flush().
More information can be found here: http://docs.angularjs.org/api/ngMock.$httpBackend
Test:
'use strict';
describe('myApp.view1 module', function() {
var $httpBackend, $rootScope, createController, jsonHandler;
beforeEach(module('myApp.view1'));
describe('view1 controller', function(){
var scope, testCont;
beforeEach(inject(function($rootScope, $controller, $injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
jsonHandler= $httpBackend.when('GET', '/view1/data.json')
.respond({data: '[XXX,XXX,XXX]'});
// Get hold of a scope (i.e. the root scope)
$rootScope = $injector.get('$rootScope');
// The $controller service is used to create instances of controllers
var $controller = $injector.get('$controller');
createController = function() {
return $controller('View1Ctrl', {'$scope' : $rootScope });
};
}));
it('should....', function(){
$httpBackend.expectGET('/view1/data.json');
var controller = createController();
$httpBackend.flush();
});
});
});

AngularJS: TypeError: undefined is not an object for localstorage

I want to test the values in localstorage. Here is the package I use to store those values: https://www.npmjs.com/package/ngstorage
Here is my code:
var module = angular.module(‘myModule', ['ngStorage']);
module.controller(‘MyController’, ['$scope', '$localStorage', function($scope,$localStorage){
$scope.storage = $localStorage;
$scope.data = {
name: “55 Cherry St.“
};
$scope.storage.name = $scope.data.name;
}]);
I want to test the above code in Jasmine and Mocha. I dont know how I can as it gives me this error now:
TypeError: undefined is not an object (evaluating 'expect($localStorage).to.be') (line 14)
Here is my test code:
describe('my Module', function () {
var $controller;
var $scope;
var element;
beforeEach(module('myModule'));
beforeEach(module('ngStorage'));
beforeEach(function() {
$scope = {};
inject(function ($controller, $rootScope, $compile, $localStorage) {
var $controller = $controller('myController', {$scope: $scope});
});
});
describe('My controller', function () {
it('should contain a $localStorage service', inject(function(
$localStorage
) {
expect($localStorage).not.to.equal(null);
}));
});
});
Jasmine doesn't have a expect('something').not.to.equal() function. Use this instead:
expect($localStorage).not.toBe(null);
Also, while reproducing your error, myController is not defined. Fixed using:
var $controller = $controller('MyController', {$scope: $scope}); // MyController (uppercase)
And I think beforeEach(module('ngStorage')); is not necessary, since it already is a dependency of your module.
You have to add a variable for it in order to test it.Below is the improvement you need to do in your code
describe('my Module', function () {
var $controller;
var $scope;
var element;
var $localStorage;
beforeEach(module('myModule'));
beforeEach(module('ngStorage'));
beforeEach(function() {
$scope = {};
inject(function ($controller, $rootScope, $compile, $localStorage) {
var $controller = $controller('myController', {$scope: $scope, $localStorage: $localStorage});
});
});
describe('My controller', function () {
it('should contain a $localStorage service', inject(function(
$localStorage
) {
expect($localStorage).not.to.equal(null);
}));
});
});
OR
expect(controller.$localStorage).not.to.equal(null); //$localStorage is part of controller now

How can I inject an Angular controller into my unit test

I'm really hoping this is a trivial issue. Yes, I have RTFM. I'm actually using the Angular documented way to inject a controller, but for some reason my controller is not defined. The main difference here is that I'm used to working on single module apps and this time I have a multi module app. I wouldn't think it would make a difference, but there you go. Instead of a lengthy description, I'll get straight to the code:
Using Angular 1.2.16
Unit Test Framework: Jasmine
app.js
angular.module('OBB', [
// Native AngularJS DI
'ngResource', 'ngCookies',
// bunch of modules
...
// OBB Page Modules
'OBB.home', 'OBB.buckets', 'OBB.company', 'OBB.advSearch', 'OBB.users'
])
So, I'm trying to test a controller in the OBB.home module.
home.js
angular.module('OBB.home', ['ui.router'])
.controller('HomeCtrl', ['$log', '$rootScope', '$scope', '$state', 'AUTH_EVENTS',
function HomeCtrl ($log, $rootScope, $scope, $state, AUTH_EVENTS) {
$scope.signInFormData = {
email: null, password: null
};
//more code...
}]);
home.spec.js
describe('Unit Home Controllers: ', function () {
var homeController, scope;
beforeEach(module('OBB.home'));
beforeEach(inject(function (_$rootScope_, $controller) {
scope = _$rootScope_.$new();
homeController = $controller('HomeCtrl', {
$rootScope: _$rootScope_,
$scope: scope,
$log: {},
$state: {},
AUTH_EVENTS: {},
});
}));
it('Home Controller is correctly instantiated', inject(function () {
expect(scope).toBeDefined(); // Pass
expect(scope.signInFormData).toBeDefined(); // Fails
}));
});
You need to mock/load your dependencies.
describe('Unit Home Controllers: ', function () {
var homeController, scope;
beforeEach(module('OBB.home'));
beforeEach(inject(function (_$rootScope_, $controller) {
scope = _$rootScope_.$new();
homeController = $controller('HomeCtrl', {
$rootScope: _$rootScope_,
$scope: scope,
$log: {}, //You will have to add methods as needed
$state: {},
AUTH_EVENTS: {}
});
}));
it('Home Controller is correctly instantiated', inject(function () {
expect(scope).toBeDefined(); // Fails
expect(scope.signInFormData).toBeDefined(); // Fails
}));
});

Jasmine unit test $scope access

Why am I unable to access my function in my Controller? The code functions like I would expect it too, however, it doesn't seem to want to allow me access to my function that I'm trying to unit test. It should just return a simple bool, but it's getting killed somewhere.
Here's some code:
RTHelper.js
describe('Unit: LocationController', function () {
var $scope, $httpBackend, $location, injector, ctrl, $controller;
//beforeEach(function () {
// angular.module('TDE').controller('LocationController'); //
// inject(function ($injector) {
// $rootScope = $injector.get('$rootScope');
// $scope = $rootScope.$new();
// //ctrl = $injector.get('$controller')("LocationController", { $scope: $scope });
// injector = $injector;
// ctrl = $injector.get('$controller');
// //scope = $injector.get('$rootScope').$new();
// $httpBackend = $injector.get('$httpBackend');
// $location = $injector.get('$location');
// });
//});
//both beforeEach methods work(which one is better? I don't know), so things are getting loaded
beforeEach(function () {
angular.module('TDE');
inject(function ($injector) {
$location = $injector.get('$location');
$rootscope = $injector.get('$rootScope');
$scope = $rootscope.$new();
$controller = $injector.get('$controller');
ctrl = function () {
return $controller('LocationController', {
'$scope': $scope
})
};
})
});
it("should just be a holder for something for later", function () {
expect($scope.BoolCondition()).toBeDefined(); //I don't care what it returns as long as it's accessed honestly
});
})
LocationController.js
angular
.module('TDE')
.controller('LocationController', ['$rootScope', '$scope', '$location', '$window', '$document', 'LocationService', 'HeaderFooterService', 'SearchService', 'TranslationService', 'MTDE_CONFIG', 'LocationPartnerAssignmentService', 'ExperimentService', function ($rootScope, $scope, $location, $window, $document, $LocationService, $HeaderFooterService, $SearchService, $TranslationService, $MTDE_CONFIG, $LocationPartnerAssignmentService, $ExperimentService) {
$scope.BoolCondition = function(myCondition){
if(//blah blah condition test on myCondition)
{
return true
}
else
{
return false;
}
}
How would I go about getting to that BoolCondition? I'm new to this so you can imagine the struggle of writing unit tests after never having done unit testing. I've also gone through countless examples and I've done some generic tests, so I'm not totally un-versed.
You're not bootstrapping the module under test correctly. You should use angular.mock.module inside the test
You're not instantiating the controller (where's the call to ctrl()?)
Here's the complete working example and the fiddle that runs it:
describe('Unit: LocationController', function () {
var $scope, $location, ctrl;
beforeEach(function () {
angular.mock.module('TDE');
inject(function (_$location_, _$rootScope_, _$controller_) {
$location = _$location_;
$scope = _$rootScope_.$new();
ctrl = _$controller_('LocationController', {
'$scope': $scope
})
});
});
it("should just be a holder for something for later", function () {
expect($scope.BoolCondition()).toBeDefined();
expect($scope.BoolCondition()).toBeTruthy();
});
})

Categories