These are my controllers and services that I am trying to cover using Jasmine testing.
home.controller.js
var home = angular.module('home',[])
home.controller('home.controller', ['$scope','$location','homeService','localStorageService',function ($scope,$location,homeService,localStorageService) {
homeService.directories(1001).then(function mySucces(_data) {
$scope.dataHome = _data;
}, function myError(response) {
alert("error");
});
$scope.func = function(fileData) {
localStorageService.set('fileId',fileData.id);
console.log(fileData.id);
$location.path("fileview");
};
}]);
home.service.js
var homeService = angular.module('homeService',[]);
homeService.factory('homeService',['$http', '$q', '$log','config', function ($http, $q, $log,config) {
var serviceAPI = {};
serviceAPI.directories = function (folderId) {
var deferred = $q.defer();
$http.get(config.service + "/filevault-service/folders").then(function mySucces(response) {
deferred.resolve(response.data.data);
}, function myError(response) {
deferred.resolve(response.status);
});
return deferred.promise;
}
return serviceAPI;
}]);
Here goes my test case:
describe('equality', function() {
var rootScope, homeService, scope, homeController, httpBackend, location, localStorageService;
beforeEach(function(){
module('fileVaultApp');
inject(function($controller, $httpBackend, $rootScope, $location, homeService, localStorageService){
rootScope = $rootScope;
scope = $rootScope.$new();
httpBackend = $httpBackend;
homeService = homeService;
location = $location;
localStorageService = localStorageService;
homeController = $controller('home.controller', { $scope: scope });
spyOn($location, 'path');
});
});
console.log("hello");
it('check the home controller', function() {
expect(1).toBe(1);
});
});
The thing is that when I am removing the "inject" snippet, the test runs good. As soon as it is injected, the problem arises.
The error has been given below:
PhantomJS 2.1.1 (Windows 7 0.0.0) equality check the home controller FAILED
C:/FileVault/file-vault/filevault-ui/static/scripts/lib-min.js:4641:53
forEach#C:/FileVault/file-vault/filevault-ui/static/scripts/lib-min.js:321:24
loadModules#C:/FileVault/file-vault/filevault-ui/static/scripts/lib-min.js:4601:12
createInjector#C:/FileVault/file-vault/filevault-ui/static/scripts/lib-min.js:4523:30
workFn#C:/FileVault/file-vault/filevault-ui/static/scripts/lib-min.js:50216:60
inject#C:/FileVault/file-vault/filevault-ui/static/scripts/lib-min.js:50196:46
C:/FileVault/file-vault/filevault-ui/tests/test_jasmine_spec.js:7:12
PhantomJS 2.1.1 (Windows 7 0.0.0): Executed 1 of 1 (1 FAILED) (0 secs / 0.012 secs)
You should inject _homeService_, and _localStorageService_;
inject(function($controller, $httpBackend, $rootScope, $location, _homeService_, _localStorageService_){
Also you should inject all the dependencies of the controller when instantiating it.
describe('equality', function() {
var rootScope, homeService, scope, homeController, httpBackend, location, localStorageService;
beforeEach(function(){
module('fileVaultApp');
inject(function($controller, $httpBackend, $rootScope, $location, _homeService_, _localStorageService_){
rootScope = $rootScope;
scope = $rootScope.$new();
httpBackend = $httpBackend;
homeService = _homeService_;
location = $location;
localStorageService = _localStorageService_;
homeController = $controller('home.controller', { $scope: scope, $location:location, homeService: homeService, localStorageService: localStorageService});
spyOn($location, 'path');
});
});
console.log("hello");
it('check the home controller', function() {
expect(1).toBe(1);
});
});
Related
Using Jasmine and chutzpah unable to access the $scope and $rootscope inside the success of http call.
In the jasmine code the var test = $rootScope.langCode is coming as 'undefined'. Which is inside the success callback of $http from factory.
Following are the controller code need to be tested.
app.controller('catalogCtrl', ['$scope', '$rootScope', '$window', 'catalogService', '$routeParams', '$location', '$timeout', '$filter', function ($scope, $rootScope, $window, catalogService, $routeParams, $location, $timeout, $filter) {
$scope.init = function (callback, params) {
catalogService.labeldata().then(function successCallback(response) {
$rootScope.langCode = "test";
}, function errorCallback(response) {
console.log(JSON.parse(JSON.stringify(response)));
$rootScope.langCode = "test1";
});
};
}]);
Following is the Factory where am doing the http call
app.factory('catalogService', ['$http', function ($http) {
return {
labeldata: function () {
return $http({
method: 'GET',
url: "/Content/Index/"
});
}
}
}]);
Jasmine code to test catalogCtrl
describe('catalogCtrl', function () {
var httpBackend, $rootScope, $scope, createController, authRequestHandler, myservice, $controller, $q;
beforeEach(module('catalogModule'));
beforeEach(function () {
module('catalogModule');
inject(function (_$controller_, _$rootScope_, $injector, _$q_) {
// inject removes the underscores and finds the $controller Provider
$controller = _$controller_;
$rootScope = _$rootScope_;
$scope = _$rootScope_.$new();
// Injecting Service references and HttpBackend Object :-
httpBackend = $injector.get('$httpBackend');
$q = _$q_;
myserv = $injector.get('catalogService');
});
});
it('Catalog Content', function () {
var $scope = {}; var $rtScope = {};
// $controller takes an object containing a reference to the $scope
var controller = $controller('catalogCtrl', { $scope: $scope, $rtScope: $rootScope });
// the assertion checks the expected result
var obj = { "COUNTRYCODE": "au", "KEY": "MERCHANDISE", "LANGCODE": "en", "VALUE": "Merchandise" };
var arr = [obj];
var returnData = {};
returnData.data = arr;
//returnData = arr;
//httpBackend.expectGET("/Content/Index/").respond(returnData);
httpBackend.when('GET', "/Content/Index/").respond(returnData);
var returnedData;
myserv.labeldata().then(function (returnData) {
// check that returned result contains
returnedData = result;
expect(returnedData).toEqual({ bar: 'foo' });
});
$scope.init();
var test = $rootScope.langCode;
});
});
I am trying to do unit test of my angular app with karma. I am getting some error. Am i missing something? A
This my controller
(function () {
"use strict"
angular
.module("myApp")
.controller("userCtrl",['$scope', '$state', 'userService', 'appSettings','md5','currentUser','$rootScope',
function ($scope, $state, userService, appSettings,md5,currentUser, $rootScope) {
$scope.login = function() {
$scope.loading = true;
if($scope.password != null){
var user ={
username:$scope.username,
password:md5.createHash($scope.password)
}
var getData = userService.login(user);
getData.then(function (response) {
console.log(response);
$scope.loading = false;
currentUser.setProfile(user.username, response.data.sessionId);
$state.go('videos');
}, function (response) {
console.log(response.data);
});
}else{
$scope.msg = "Password field is empty!"
}
}
}])
}());
This is my test codes
'use strict';
describe('userCtrl', function() {
beforeEach(module('myApp'));
var scope, userCtrl, apiService,q, deferred, currentUser;
describe('$scope.login', function(){
beforeEach(function(){
apiService = {
login: function () {
deferred = q.defer();
return deferred.promise;
};
};
});
beforeEach(inject(function($controller, $rootScope, $q, _currentUser_){
var user ={name:'ali',password:'password'};
scope = $rootScope.$new();
q = $q;
// The injector unwraps the underscores (_) from around the parameter names when matching
userCtrl = $controller('userCtrl', {
$scope:scope,
userService:apiService
});
//userService = _userService_;
currentUser = _currentUser_;
}));
it('should call user service login', function() {
spyOn(apiService, 'login').and.callThrough();
scope.login();
deferred.resolve(user);
expect(apiService.login).toHaveBeenCalled();
});
it('checks the password field', function() {
scope.login();
expect(scope.msg).toEqual('Password field is empty!');
});
});
});
And i am getting this error
enter image description here
If you have to test controller then use to spyon for service method and in case of service then use HttpBackend
describe('Testing a Controller that uses a Promise', function() {
var $scope;
var $q;
var deferred;
beforeEach(module('search'));
beforeEach(inject(function($controller, _$rootScope_, _$q_, searchService) {
$q = _$q_;
$scope = _$rootScope_.$new();
// We use the $q service to create a mock instance of defer
deferred = _$q_.defer();
// Use a Jasmine Spy to return the deferred promise
spyOn(searchService, 'search').and.returnValue(deferred.promise);
// Init the controller, passing our spy service instance
$controller('SearchController', {
$scope: $scope,
searchService: searchService
});
}));
it('should resolve promise', function() {
// Setup the data we wish to return for the .then function in the controller
deferred.resolve([{
id: 1
}, {
id: 2
}]);
// We have to call apply for this to work
$scope.$apply();
// Since we called apply, not we can perform our assertions
expect($scope.results).not.toBe(undefined);
expect($scope.error).toBe(undefined);
});
});
This for same using spyon for service method then use $appy method to make it work.
I'm trying to test a method that deletes an item from a list after user confirmation.
Controller:
app.controller('mainCtrl', ['$scope', '$window', 'dataService', function($scope, $window, dataService) {
var vm = this;
vm.delete = function(id, index) {
if($window.confirm('Are you sure?')) {
dataService.deleteById(id).then(function() {
vm.list.splice(index, 1)
});
}
};
}]);
Sevice:
app.service('dataService', ['$http', function($http) {
this.deleteById = function(id) {
return $http.delete('delete-item?id=' + id);
};
}]);
Test:
describe('Testing RecipesController', function() {
var scope, ctrl, dataServiceMock, q, deferred, window;
beforeEach(function() {
dataServiceMock = {
deleteById: function() {
deferred = q.defer();
return deferred.promise;
}
};
});
beforeEach(function() {
module('app');
inject(function($rootScope, $controller, $q, $window) {
q = $q;
window = $window;
scope = $rootScope.$new();
ctrl = $controller('mainCtrl', {
$scope: scope,
dataService: dataServiceMock
});
});
});
it('should delete recipe if the user clicked "OK"', function() {
spyOn(window, 'confirm').and.returnValue(true);
spyOn(dataServiceMock, 'deleteById').and.callThrough();
var item= {
id: 2,
name: 'Shirt'
};
ctrl.list = ['Hat', 'Shirt'];
ctrl.delete(item, 1);
expect(dataServiceMock.deleteById).toHaveBeenCalled();
expect(ctrl.list.length).toBe(1);
});
});
I successfully mocked the confirm dialog and the delete method, and the test to check if the method been called even passes.
But, The promise.then() isn't working.
After I run the test I got this message "Expected 2 to be 1".
I see one thing for sure, which is that you never resolve or reject your promise in the data service mock. Try changing the mock to this:
beforeEach(function() {
dataServiceMock = {
deleteById: function() {
deferred = q.defer();
deferred.resolve({ /* whatever data you want to resolve with */ });
return deferred.promise;
// You could also shorten this whole mock function to just:
// return $q.resolve({ /* some data */ });
}
};
});
Also, don't forget to execute the $digest() function on the $rootScope at the end of your test... you're actually executing it on your controller's scope, NOT the root scope.
Hold onto the actual $rootScope object - change your beforeEach to:
var $rScope;
beforeEach(function() {
module('app');
inject(function($rootScope, $controller, $q, $window) {
q = $q;
window = $window;
$rScope = $rootScope;
ctrl = $controller('mainCtrl', {
$scope: $rootScope.$new(),
dataService: dataServiceMock
});
});
});
Then in your test, execute $digest on the root scope at the end:
it('should delete recipe if the user clicked "OK"', function() {
// all your test codez...
$rScope.$digest();
});
I am trying to write a unit test to test a simple factory that performs a http.get to retrieve a JSON file.
The factory is called within my controller.
Here's a plunker showing my http.get: http://plnkr.co/edit/xg9T5H1Kreo4lwxzRQem?p=preview
Ctrl:
app.controller('MainCtrl', function($scope, $http, factoryGetJSONFile) {
factoryGetJSONFile.getMyData(function(data) {
$scope.Addresses = data.Addresses.AddressList;
$scope.People = data.Names.People;
});
});
Factory:
app.factory('factoryGetJSONFile', function($http) {
return {
getMyData: function(done) {
$http.get('data.json')
.success(function(data) {
done(data);
})
.error(function(error) {
alert('An error occured whilst trying to retrieve your data');
});
}
}
});
Test:
// ---SPECS-------------------------
describe('with httpBackend', function () {
var app;
beforeEach(function () {
app = angular.mock.module('plunker')
});
describe('MyCtrl', function () {
var scope, ctrl, theService, httpMock;
beforeEach(inject(function ($controller, $rootScope, factoryGetJSONFile, $httpBackend) {
scope = $rootScope.$new(),
ctrl = $controller('MyCtrl', {
$scope: scope,
factoryGetJSONFile: theService,
$httpBackend: httpMock
});
}));
it("should make a GET call to data.json", function () {
console.log("********** SERVICE ***********");
httpMock.expectGET("data.json").respond("Response found!");
//expect(factoryGetJSONFile.getMyData()).toBeDefined();
httpMock.flush();
});
})
});
Error:
TypeError: 'undefined' is not an object (evaluating 'httpMock.expectGET')
You should assign $httpBackend to httpMock in beforeEach like this:
beforeEach(inject(function ($controller, $rootScope, factoryGetJSONFile, $httpBackend) {
httpMock = $httpBackend;
scope = $rootScope.$new();
ctrl = $controller('MyCtrl', {
$scope: scope,
factoryGetJSONFile: factoryGetJSONFile,
$httpBackend: httpMock
});
}));
My controller:
angularMoonApp.controller('SourceController', ['$scope', '$rootScope', '$routeParams', 'fileService', function ($scope, $rootScope, $routeParams, fileService) {
$scope.init = function() {
$rootScope.currentItem = 'source';
fileService.getContents($routeParams.path).then(function(response) {
$scope.contents = response.data;
$scope.fileContents = null;
if(_.isArray($scope.contents)) {
// We have a listing of files
$scope.breadcrumbPath = response.data[0].path.split('/');
} else {
// We have one file
$scope.breadcrumbPath = response.data.path.split('/');
$scope.breadcrumbPath.push('');
$scope.fileContents = atob(response.data.content);
fileService.getCommits(response.data.path).then(function(response) {
$scope.commits = response.data;
});
}
});
}
$scope.init();
}]);
My unit test:
(function() {
describe('SourceController', function() {
var $scope, $rootScope, $httpBackend, $routeParams, $q, createController, fileService, deferred;
beforeEach(module('angularMoon'));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
$rootScope = $injector.get('$rootScope');
$routeParams = $injector.get('$routeParams');
$scope = $rootScope.$new();
$q = $injector.get('$q');
deferred = $q.defer();
fileService = $injector.get('fileService');
var $controller = $injector.get('$controller');
createController = function() {
return $controller('SourceController', {
'$scope': $scope,
'$routeParams': $routeParams,
'fileService': fileService
});
};
}));
it("should set the current menu item to 'source'", function() {
createController();
$scope.init();
expect($rootScope.currentItem).toBe('source');
});
it("should get test the getContents call of the fileService", function() {
spyOn(fileService, 'getContents').andCallThrough();
createController();
$scope.init();
expect(fileService.getContents).toHaveBeenCalled();
});
it("should return an object with multiple files", function() {
var multipleFiles = [{path: '.DS_Store'}, {path: '.bowerrc'}];
deferred.resolve(multipleFiles);
spyOn(fileService, 'getContents').andReturn(deferred.promise);
createController();
$scope.init();
expect($scope.contents).toBe(multipleFiles);
expect($scope.breadcrumbPath).toBe('');
});
});
})();
The last test fails with:
Expected undefined to be [ { path : '.DS_Store' }, { path : '.bowerrc' } ].
Expected undefined to be ''.
Why is the $scope undefined here?
Your controller is expecting you to inject in $rootScope which you are not doing in your unit test.
You have:
createController = function() {
return $controller('SourceController', {
'$scope': $scope,
'$routeParams': $routeParams,
'fileService': fileService
});
But but should have:
createController = function() {
return $controller('SourceController', {
'$scope': $scope,
'$rootScope': $rootScope,
'$routeParams': $routeParams,
'fileService': fileService
});
Also, you will want to call this code:
createController();
$scope.init();
before you resolve your promise:
deferred.resolve(multipleFiles);
The scope is not undefined. What is undefined is $scope.contents and $scope.breadcrumbPath.
And that's because promise callbacks are always being called asynchronously. You need to call
$scope.$apply()
before verifying your expectations.