How to write jasmine test for Rectangular for a scenario like as shown below
script
$scope.allEmployees = [{
visible: true,
empId: "EMP148"
}];
$scope.employeeDocuments = { "EMP148": [{
"empId": "EMP148",
"department": "Sales",
"firstName": "Manu",
"lastName": "Mathew",
"place": "Kolkata"
}]
};
var employeesCopy = angular.copy($scope.allEmployees);
$scope.allEmployees.push({visible: true, empId: "EMP489"});
$scope.addEmployees = function (employeesCopy) {
var newEmployee = {visible: true, empId: "EMP489"};
var emplyeeList = {
employees: $scope.allEmployees,
newEmployee: newEmployee
};
Restangular.all('emp/getEmployees').post(emplyeeList).then(function (employees) {
if (!_.isEmpty(employees[emplyeeList.newEmployee.empId])) {
if ($scope.employeeDocuments.hasOwnProperty(emplyeeList.newEmployee.empId)) {
delete $scope.employeeDocuments[emplyeeList.newEmployee.empId];
}
$scope.employeeDocuments[emplyeeList.newEmployee.empId] = employees[emplyeeList.newEmployee.empId];
setMyEmployees(true);
$scope.flag = true;
console.log("success");
} else {
$scope.employeeDocuments = employeesCopy;
console.log("no documents");
}
$scope.flag = false;
}, function (error) {
console.log("failed");
$scope.flag = false;
});
};
$scope.addEmployees(employeesCopy);
setMyEmployees = function (flag)
{
// other implementation
};
I have wrote a test cases like as shown below, but i am getting exception like spyOn could not find an object to spy upon for all()
test case
WORKING DEMO - contains the full logic as well as the test cases
describe('Testing Controllers', function() {
describe('Testing EmployeeController Controller', function() {
var EmployeeController, $scope, $httpBackend, Restangular;
beforeEach(function() {
module('emp');
});
beforeEach(inject(function($controller, $rootScope, $filter, $injector, Restangular, $httpBackend) {
$scope = $rootScope.$new();
$httpBackend = $httpBackend;
Restangular = $injector.get("Restangular");
EmployeeController = $controller('EmployeeController ', {
$rootScope: $rootScope,
$scope: $scope,
$filter: $filter
});
}));
it('should add new employees when addEmployees() is called', inject(function($httpBackend)
{
$scope.allEmployees = [{
visible: true,
empId: "EMP148"
}];
$scope.employeeDocuments = { "EMP148": [{
"empId": "EMP148",
"department": "Sales",
"firstName": "Manu",
"lastName": "Mathew",
"place": "Kolkata"
}]
};
var employeesCopy = angular.copy($scope.allEmployees);
spyOn(Restangular, 'all').andCallThrough();
var newEmployee = {visible: true, empId: "EMP489"};
var emplyeeList = {
employees: $scope.allEmployees,
newEmployee: newEmployee
};
var mockToReturn = {
"EMP489": [{
"empId": "EMP489",
"department": "Sales",
"firstName": "Ram",
"lastName": "Mohan",
"place": "Delhi"
}]
};
$scope.addEmployees(employeesCopy);
$httpBackend.expectPOST('emp/getEmployees',emplyeeList).respond(mockToReturn);
expect(Restangular.all).toHaveBeenCalledWith('emp/getEmployees');
expect(setMyEmployees(true)).toHaveBeenCalled();
}));
});
});
You have multiples problem :
First, to answer your question to use toHavebeenCalled you have to create a spy first.
What I would do is put setMyEmployees at the scope level, and then add a spy on the scope
spyOn($scope);
But you have a test that make an asyncronous request, so your test case will fail because it will reach the end before the asynchronous request succeed.
With jasmine 2 you can use done() for asynchonous test :
it('should add new employees when addEmployees() is called', function(done)
{
//call when asyncronous operation is finish
done();
}
But with the way you've created your function, you can't use done. You will have to have a callback block in your method or a promise
Callback :
$scope.addEmployees = function(employeesCopy, success, failure)
{
//code
Restangular.all('user/getEmployees').post(emplyeeList).then(function(employees)
{
if (!_.isEmpty(employees[emplyeeList.employeeId]))
{
// code
}
else
{
// code
}
success();
$scope.flag = false;
}, function(error)
{
failure();
$scope.flag = false;
});
};
Note the toHaveBeenCall syntax
it('should add new employees when addEmployees() is called', function(done)
{
var employeesCopy = {
firstName: "Manu",
lastName: "Sam"
};
spyOn($scope);
$scope.addEmployees(employeesCopy, function(){
done();
});
expect($scope.setMyEmployees).toHaveBeenCalledWith(true);
});
I would prefer a promise syntax :
$scope.addEmployees = function(employeesCopy, defer)
{
//code
Restangular.all('user/getEmployees').post(emplyeeList).then(function(employees)
{
if (!_.isEmpty(employees[emplyeeList.employeeId]))
{
// code
}
else
{
// code
}
defer.resolve();
$scope.flag = false;
}, function(error)
{
defer.reject();
$scope.flag = false;
});
};
it('should add new employees when addEmployees() is called', function(done)
{
var employeesCopy = {
firstName: "Manu",
lastName: "Sam"
};
spyOn($scope);
var defer = $q.defer;
defer.promise.then(function(){
console.log("success");
done();
}, function (){
done();
console.log("error");
});
$scope.addEmployees(employeesCopy, defer);
expect($scope.setMyEmployees).toHaveBeenCalledWith(true);
});
The last problem you will have is to mock the network call if you want your test to be truly unitary (else you'r also testing the backend answer, that could be what you want) If you want to mock the call you should look at $httpBackend, this blog post seems to have more informations : https://ath3nd.wordpress.com/2013/08/05/15/ (not mine)
EDIT, add dependancy to test, before your it(), use a beforeEach :
var myService, Restangular;
beforeEach(function() {
inject(function($injector) {
myService = $injector.get('MyService');//exemple service
Restangular = $injector.get("Restangular");
});
});
EDIT 2 :
Ok I havn't explain it correctly, try :
beforeEach(inject(function($controller, $rootScope, $filter, $injector, _Restangular_, _$httpBackend_) {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
Restangular = _Restangular_;
EmployeeController = $controller('EmployeeController ', {
$rootScope: $rootScope,
$scope: $scope,
$filter: $filter
});
}));