angular test, can't find variable : $scope - angularjs

I did read the angular official tutorial and launch test with success. But I tried to make test for a simple controller (when you click on button a boolean change), i have the following error : ReferenceError: Can't find variable: $scope
controller :
siteLogic = angular.module('siteLogic', []);
siteLogic.controller('siteCtrl', ['$scope',function($scope, initMap) {
$scope.addingSite=false;
$scope.changeAddingSite = function(){
$scope.addingSite= !$scope.addingSite;
};
}]);
file for test :
describe('$scope initialisation', function() {
beforeEach(module('siteLogic'));
describe('sets the addingSite at the inverse', function() {
var scope, controller;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
//it's on the next line I have the error indication
controller = $controller('siteCtrl', { $scope: scope });
}));
it('sets true if false', function() {
scope.addingSite = false;
scope.changeAddingSite();
expect(scope.addingSite).toEqual(true);
});
it('sets false if true', function() {
$scope.addingSite = true;
$scope.changeAddingSite();
expect($scope.addingSite).toEqual(false);
});
});
});
karma.config.js
module.exports = function(config) {
config.set({
basePath: '..',
frameworks: ['jasmine'],
files: [
'test/dep/angular-1.3.15/angular.js',
'test/dep/angular-1.3.15/angular-resource.js',
'test/dep/angular-1.3.15/angular-route.js',
'test/dep/angular-1.3.15/angular-mocks.js',
'lib/map/public/angular/*.js',
'test/map/*.js'
],
autoWatch: true,
browsers: ['PhantomJS']
});
};

In the second group of test change $scope to scope
it('sets false if true', function() {
$scope.addingSite = true; // there is no $scope
$scope.changeAddingSite();
expect($scope.addingSite).toEqual(false);
});
var siteLogic = angular.module('siteLogic', []);
siteLogic.controller('siteCtrl', ['$scope',function($scope) {
$scope.addingSite=false;
$scope.changeAddingSite = function(){
$scope.addingSite= !$scope.addingSite;
};
}]);
describe('$scope initialisation', function() {
beforeEach(module('siteLogic'));
describe('sets the addingSite at the inverse', function() {
var scope, controller;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
//it's on the next line I have the error indication
controller = $controller('siteCtrl', { $scope: scope });
}));
it('sets true if false', function() {
scope.addingSite = false;
scope.changeAddingSite();
expect(scope.addingSite).toEqual(true);
});
it('sets false if true', function() {
scope.addingSite = true;
scope.changeAddingSite();
expect(scope.addingSite).toEqual(false);
});
});
});
<link href="http://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet"/>
<script src="http://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

Related

How to write unit testcase for controller without using $scope in it

I need to write a test case for a method in a controller. In that controller I'm using this instead of $scope. If we use $scope we can able to write the test case as below. But how can i write a test case if i am using this in the controller.
app.controller('ExampleController', function(){
var test = this;
this.testFunction = function(){
return "Hello";
}
});
karma-test case file
describe('app module', function () {
beforeEach(module('testAngularApp'));
describe('ContentController', function () {
var scope, controller;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
controller('ContentController', {
$scope: $scope
});
it('Should return Hello', function () {
expect(scope.testFunction ()).toBe(true);
});
});
Here you go:
//--- CODE --------------------------
(function(angular) {
angular.module('myApp', [])
.controller('ExampleController', function() {
var vm = this;
vm.data = "HI!";
this.testFunction = function(val) {
vm.data = val;
}
});
})(angular);
// ---SPECS-------------------------
describe('myApp', function() {
describe('Example Controller', function() {
var scope, controller;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('ExampleController', {
$scope: scope
});
spyOn(controller, 'testFunction').and.callThrough();
}));
it('expect controller should be defined', function() {
expect(controller).toBeDefined();
});
it('expect scope should be defined', function() {
expect(scope).toBeDefined();
});
it('expect data should be initialized', function() {
expect(controller.data).toEqual("HI!");
});
it('expect data is updated when testFunction is called', function() {
controller.testFunction('Bye!');
scope.$apply();
expect(controller.testFunction).toHaveBeenCalled();
expect(controller.data).toEqual("Bye!");
});
});
});
// --- Runner -------------------------
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
}());
<body>
<!-- because we are testing our controller and not running we don't need a controller or even a module -->
<!-- so there is no ng-app or ng-controller in the markup -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine.css">
<!-- the order that these files load is critical, think twice before changing -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine-html.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/boot.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-mocks.js"></script>
<h2>Finished jasmine unit test</h2>
</body>

How to test $scope.$watch properly with Jasmine in AngularJS

I'm new to AngularJS and unit testing,
I'm testing a list that gets changing by selected category.
The test is passing but only if I use the httpBackend.expectGET() that expects the XMLHttpRequest from the "getSomethingElse" method.
I also tried to use the scope.$digest() but I got the same results...
The Controller:
app.controller('mainCtrl', ['$scope', 'myService', function($scope,
myService) {
$scope.category = null;
myService.getSomethingElse().then(function(res) {
$scope.somethingElse = res.data;
});
$scope.$watch('category', function() {
if ($scope.category !== null) {
myService.getListByCat($scope.category.name).then(function(res) {
$scope.list = res.data;
});
}
else {
myService.getLongList().then(function(res) {
$scope.list = res.data;
});
}
});
}]);
The Service:
app.service('myService', ['$http', function($http) {
this.getListByCat = function(category) {
return $http.get('getting-list?cat=' + category);
};
this.getLongList = function() {
return $http.get('getting-long-list');
};
this.getSomethingElse = function() {
return $http.get('getting-something-else');
};
}]);
The Test
describe('Testing mainCtrl', function() {
var scope, ctrl;
var myServiceMock = {
getSomethingElse: jasmine.createSpy().and.returnValue(1),
getListByCat: jasmine.createSpy().and.returnValue(2)
};
beforeEach(function() {
module('app');
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('mainCtrl', {
$scope: scope,
myService: myServiceMock
});
});
});
it('should update the list by selected category', function() {
expect(scope.category).toBeNull();
expect(scope.list).toBeUndefined();
scope.category = {
id: 1,
name: 'Jobs'
};
scope.$apply();
expect(myServiceMock.getSomethingElse).toHaveBeenCalled();
expect(myServiceMock.getListByCat).toHaveBeenCalled();
});
});
The test is passing but only if I use the httpBackend.expectGET() that expects the XMLHttpRequest from the "getSomethingElse" method.
This is because your myServiceMock is not replacing the original myService. You have various ways to test this - one of them is given below. Here we are replacing myService with the service mock:-
beforeEach(function() {
module('app');
module(function($provide){
$provide.factory('myServiceMock',
function(){
return myServiceMock;
);
});
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('mainCtrl', {
$scope: scope,
myService: myServiceMock
});
});
});
You can add your watcher like this.
$scope.categoryWatcher = categoryWatcher;
$scope.$watch('category', categoryWatcher);
function categoryWatcher() {
if ($scope.category !== null) {
myService.getListByCat($scope.category.name).then(function(res) {
$scope.list = res.data;
});
}
else {
myService.getLongList().then(function(res) {
$scope.list = res.data;
});
}
}
and in Unit testing just create new it construct for that handler
it('should test categoryWatcher for null value', function(){
$scope.category = null;
$scope.categoryWatcher();
// your expectations
});
it('should test categoryWatcher for "desiredValue" value', function(){
$scope.category = "desiredValue";
$scope.categoryWatcher();
// your expectations
});
that way, if&else clauses will be taken in the test.

'No more request expected' error when using $httpBackend

I'm trying to test a simple call to my API, and I'm going round in circles trying to work out why it's failing.
I've simplified things a bit.
This would be the error for the test below:
Error: Unexpected request: GET /api/search?blah=something
No more request expected
Here is the test:
it('does what it should', function() {
httpBackend.expectGET('/api/search?blah=something').respond(aTestResponse);
scope.search();
httpBackend.flush();
// expectations here...
});
The search function in the controller:
function search() {
myDataService.getSearchResults().query(mySearchParams, function(response) {
// do stuff
}
}
and the data service function:
function getSearchResults() {
return $resource('/api/search', {
param1: '#param1',
param2: '#param2',
...etc
});
}
Any suggestions would be really appreciated.
Edit - here is an edited, but more complete version of my spec file:
'use strict';
describe('Controller: BlahCtrl', function() {
beforeEach(module('blahApp'));
beforeEach(module(function($urlRouterProvider) {
$urlRouterProvider.deferIntercept();
}));
var BlahCtrl;
var scope;
var rootScope;
var httpBackend;
beforeEach(inject(function($controller, $rootScope, $httpBackend) {
httpBackend = $httpBackend;
scope = $rootScope.$new();
rootScope = $rootScope;
BlahCtrl = $controller('BlahCtrl as vm', {
$scope: scope
});
this.testResults = [
{
testProperty1: 'test-value-1-1',
testProperty2: 'test-value-1-2',
testProperty3: 'test-value-1-3'
},
{
testProperty1: 'test-value-2-1',
testProperty2: 'test-value-2-2',
testProperty3: 'test-value-2-3'
}
];
}));
beforeEach(function() {
this.addMatchers({
toEqualData: function(expected) {
return angular.equals(this.actual, expected);
}
});
});
it('stores the search results', function() {
httpBackend.expectGET('/api/search?blah=something').respond(this.testResults);
scope.vm.doSearch();
httpBackend.flush();
// expectations here...
});
});

Unit testing controller with injected dependencies

What is the best practice to inject the dependencies into my
controller test?
When would I use the module(function($provide){})?
How do I properly check that $state.go() was called with the right arguments?
example-controller.js
angular.module('myModule')
.controller('ExampleCtrl', ['$state', 'ExampleService', 'exampleResolve',
function($state, ExampleService, exampleResolve){
var self = this;
self.property = false;
self.resolvedProperty = exampleResolve;
self.submit = function() {
ExampleService
.exampleGet()
.$promise
.then(function(res) {
$state.go('anotherView', { prop1: 'yay', prop2: 'again' });
})
};
}]);
example-controller.test.js
describe('Controller: ExampleCtrl', function() {
beforeEach(module('myModule'));
var ctrl,
mockBackend,
mockState;
var mockExampleResolve = { test: 'Test' };
// Provide any mocks needed
// when do I provide mocks?
beforeEach(function() {
module(function($provide) {
});
});
beforeEach(inject(function($controller, $httpBackend, exampleResolve, $state) {
mockBackend = $httpBackend;
mockState = $state;
exampleResolve = mockExampleResolve;
ctrl = $controller('ExampleCtrl');
}));
describe('initialization', function() {
beforeEach(function() {});
it('should exist', function() {
expect(!!ctrl).toBe(true);
});
it('should initialize any view-model variables', function() {
expect(ctrl.property).toBe('false');
expect(ctrl.resolvedProperty).toEqual({test: 'Test'});
});
});
describe('submit called', function() {
beforeEach(function() {
});
it('should call state.go with the correct arguments', function() {
// how do i check this?
});
});
});
You can use spyOn method from jasmine to check if your parameters are correct.
describe('Controller: ExampleCtrl', function() {
beforeEach(module('myModule'));
var ctrl,
mockBackend,
mockState;
var mockExampleResolve = { test: 'Test' };
// Provide any mocks needed
// when do I provide mocks?
beforeEach(function() {
module(function($provide) {
});
});
beforeEach(inject(function($controller, $httpBackend, exampleResolve, $state) {
mockBackend = $httpBackend;
mockState = $state;
spyOn($state,'go');
exampleResolve = mockExampleResolve;
ctrl = $controller('ExampleCtrl');
}));
describe('initialization', function() {
beforeEach(function() {});
it('should exist', function() {
expect(!!ctrl).toBe(true);
//Here you can pass your param and state
expect($state.go).toHaveBeenCalledWith('anotherView', { prop1: 'yay', prop2: 'again' });
});
it('should initialize any view-model variables', function() {
expect(ctrl.property).toBe('false');
expect(ctrl.resolvedProperty).toEqual({test: 'Test'});
});
});
describe('submit called', function() {
beforeEach(function() {
});
it('should call state.go with the correct arguments', function() {
// how do i check this?
});
});
});

Unit testing call a function of a controller with Karma and Jasmine

This is my angular controller :-
angular.module('authoring-controllers', []).
controller('NavCtrl', function($scope, $location, BasketNavigationService) {
$scope.test= function() {
$scope.testVar = BasketNavigationService.showBasketList();
};
});
TEST class
describe('NavCtrl', function() {
var scope, $location, createController;
beforeEach(inject(function ($rootScope, $controller, _$location_) {
$location = _$location_;
scope = $rootScope.$new();
createController = function() {
return $controller('NavCtrl', {
'$scope': scope
});
};
}));
it('should create $scope.testVar when calling test',
function() {
expect(scope.testVar).toBeUndefined();
scope.test();
expect(scope.testVar).toBedefined();
});
});
Getting an error when i run that test case :- scope.test() is undefined..
If i removed BasketNavigationService functionality from controller then it is working..
Please help me to solve that karma test case.
$scope.showBasketList is not a function that can be invoked with $scope.showBasketList(). It is a variable that is equal to the return value of BasketNavigationService.showBasketList().
If you want to reference that function instead it should be $scope.showBasketList = BasketNavigationService.showBasketList; in your controller.
here is the working demo , hope it helps. problem was with injecting the dependencies.
//--- CODE --------------------------
(function(angular) {
// Create module
var myApp = angular.module('myApp', []);
// Controller which counts changes to its "name" member
myApp.controller('MyCtrl', ['$scope', 'BasketNavigationService',
function($scope, BasketNavigationService) {
$scope.test = function() {
$scope.testVar = BasketNavigationService.showBasketList();;
};
}
]);
})(angular);
// ---SPECS-------------------------
describe('myApp', function() {
var scope,
controller;
beforeEach(function() {
module('myApp');
});
describe('MyCtrl', function() {
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope,
'BasketNavigationService': {
showBasketList: function() {
return null;
}
}
});
}));
it('should create $scope.testVar when calling test',
function() {
expect(scope.testVar).toBeUndefined();
scope.test();
// scope.$digest();
expect(scope.testVar).toBeDefined();
});
});
});
// --- Runner -------------------------
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
<script src="http://jasmine.github.io/1.3/lib/jasmine.js"></script>
<script src="http://jasmine.github.io/1.3/lib/jasmine-html.js"></script>
<script src="https://code.angularjs.org/1.2.9/angular.js"></script>
<script src="https://code.angularjs.org/1.2.9/angular-mocks.js"></script>
<link href="http://jasmine.github.io/1.3/lib/jasmine.css" rel="stylesheet" />
fiddle : http://jsfiddle.net/invincibleJai/pf1deoom/1/

Resources