Jasmine - Spying on a methods - jasmine

I want to test a service call loginUser inside a LoginController.
Now it is fine if loginUser is global method.
but i am trying to test it after the login button was click.
Controller
$scope.login = function () {
UtilsFactory.showLoader();
LoginService.loginUser($scope.data.username, $scope.data.password, false).success(function (data) {
UtilsFactory.hideLoader();
if ($scope.loginSuccessfull(data)) {
$location.path('/tab');
} else {
UtilsFactory.hideLoader();
UtilsFactory.popup('Login failed!', data.Message);
}
}).error(function (data) {
UtilsFactory.hideLoader();
UtilsFactory.popup('Login failed!', 'Login credentials failed!');
});
}
Tests
describe("Unit: Login Controllers", function () {
var $rootScope, $scope, $controller, LoginService;
// load the controller's module
beforeEach(module('starter'));
beforeEach(inject(function (_$rootScope_, _$controller_, _LoginService_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$controller = _$controller_;
LoginService = _LoginService_;
$controller('LoginController', {
'$rootScope': $rootScope,
'$scope': $scope,
'LoginService': LoginService
});
}));
//Success
it("should call the login method on the LoginController", function () {
spyOn($scope, "login");
$scope.login();
expect($scope.login).toHaveBeenCalled();
});
//Fails
it("calls the loginUser() function", function () {
spyOn(LoginService, "loginUser");
spyOn($scope, "login");
$scope.login();
expect(LoginService.loginUser).toHaveBeenCalled();
});
});
and the error i am getting is
calls the loginUser() function
Error: Expected a spy, but got undefined.
I understand why i am getting it, just don't know how to fix it.

spyOn does no return a spy, but replaces a method of your object with a spy. So your spy variable gets undefined, because again spyOn does not return anything, and that's why you get this error. Instead, you should pass a method which is being replaced by a spy to expect:
// replaces method 'loginUser' in LoginService object
spyOn(LoginService, 'loginUser');
// ...
// pass a spy to expect
expect(LoginService.loginUser).toHaveBeenCalled();
Jasmine docs for spyOn

Related

How to test a callback of a mocked function with jasmine?

A method of a third party service that I am using has a callback as the second argument. This callback is executed in real life when the response is received from the server.
I want to mock the third party method for unit testing, and supply different response arguments to the callback to ensure that its logic executes correctly. For example to check that the promise is rejected when the status is NOT 'success', or that on success just the saved record is returned and not the whole response.
I am using jasmine for testing.
function save() {
var deferred = $q.defer();
thirdPartyService.doSave(record, function callback(response) {
// How to test the code in here when doSave is mocked?
if(response.status === 'success') {
deferred.resolve(response.savedRecord);
} else {
deferred.reject(response);
}
});
return deferred.promise;
}
Example of a test I'd like to run:
// Setup
const successResponse = {
status: 'success',
savedRecord: { Id: 'test-id' }
};
// Somehow config the mocked thirdParty.doSave() to use successResponse for the callback.
// Test
myService.save()
.then(function(response) {
expect(response.Id).toBe('test-id');
});;
You could mock thirdParty.doSave using spyOn.and.callFake.
const successResponse = {
status: 'success',
savedRecord: { Id: 'test-id' }
};
spyOn(thirdParty, 'doSave').and.callFake((record, callback) => callback(successResponse));

Karma/Jasmin Test Promise error inside of regular function

First my Setup. I try to test some of my Ionic Pages etc.
And I have a login page where I want to test the Login.
This is my Login method:
doLogin() {
let authHelper = this.injector.get(AuthHelper);
authHelper.sendSignInLink(this.email).then(()=> {
window.localStorage.setItem("userMail", this.email);
let loginToast = this.toast.create({
message: "Link to email succesfully send. In order to finish the login, it is necessary to click the send link.",
duration: 3000,
position: "bottom",
});
loginToast.present();
}).catch(() => {
throw new EmailSignInLinkError("invalid Mail");
});
}
As we can see the method sendSignInLink is returning a promise and if the promise is rejected it should throw a custom error.
For my unit test I mocked the authHelperClass to a very simple mock:
export class AuthHelperMock {
public auth(): Promise<any> {
return new Promise((resolve, reject) => {
resolve(2);
});
}
public sendSignInLink(email: String): Promise<any> {
return new Promise((resolve, reject) => {
if (email != undefined || email != "")
resolve(1);
else
reject("Invalid email");
});
}
}
Now is the Problem that I try to check on this thrown error from my promise.
My it case is:
it('should not do the login', () => {
component.email = "";
expect(() => {
component.doLogin();
}).toThrowError(EmailSignInLinkError)
I know that this will not work because the promise is async and the case will fail before it throws the error. But I don't find any suitable solution beside changing my doLogin to a promise as well.
How can I check on this Promise within a regular function?

.then is not a function Angularjs factory

I just started learning Jasmine test cases for angularjs. I am unable to test below code.Kindly help
$scope.getConstants = function(lovName) {
ConstantService.getConstants(lovName).then(function(d) {
switch (lovName) {
case 'WORKFLOW':
$scope.workflowTypes = d;
$scope.loadCounterpartyTmp();
break;
--------Other Cases
}
My ConstantService is defined as
App.factory('ConstantService', [ '$http', '$q', function($http, $q) {
return {
getConstants : function(lovName) {
return $http.post('/sdwt/data/getConstants/', lovName).then(function(response) {
return response.data;
}, function(errResponse) {
return $q.reject(errResponse);
});
}
I want to test getConstants function.I need to create a mock of ConstantService and pass the data to it.
I have written below test case but the test case is not working.Please let me know how to test the above code
describe('getConstantsForMurexEntity', function() {
it('testing getConstantsForMurexEntity function', function() {
var d=[];
d.push(
{id:1,value:'ABC'},
{id:2,value:'DEF'},
{id:3,value:'IJK'},
{id:4,value:'XYZ'},
);
//defined controller
spyOn(ConstantService, 'getConstants').and.returnValue(d);
$scope.getConstants('WORKFLOW');
expect($scope.workflowTypes).toBe(d);
The above test case is not working as it is saying "ConstantService.getConstants(...).then is not a function".
Your ConstantService.getConstants() function returns a promise, which your actual code is using, with the .then() call. This means means that when you spy on it, you also need to return a promise, which you are not doing. Because you are not returning a promise, when your actual call tries to call .then(), it is undefined, which is the reason for the error message.
Also, you aren't using Array.push correctly.
Your test should probably look something like the following (note, this is untested):
describe('getConstantsForMurexEntity', function() {
it('should set workflowTypes to the resolved value when lovName is "WORKFLOW"', inject(function($q) {
var deferred = $q.defer();
spyOn(ConstantService, 'getConstants').and.returnValue(deferred.promise);
var d = [
{id:1,value:'ABC'},
{id:2,value:'DEF'},
{id:3,value:'IJK'},
{id:4,value:'XYZ'},
];
$scope.getConstants('WORKFLOW');
deferred.resolve(d);
$scope.$apply();
expect($scope.workflowTypes).toBe(d);
}));
});

Using Jasmine spyOn to mock function in Browserify module

I'm trying to run a unit test on a function (testFunc). testFunc calls another function (secondFunc) which I would like to mock. Can I mock secondFunc so that when it is called in the context of testFunc, the spiedOn version of secondFunc is called? If not, how should I reformat my browserify module to make it testable?
Currently the setup looks something like this:
app.js (Browserify Module)
module.exports = (function () {
function testFunc() {
secondFunc();
}
function secondFunc(){
console.log('not mocked!');
}
return {
testFunc, secondFunc
};
})();
test.js (Jasmine Test)
describe("testFunc", () => {
let app = require('./app');
beforeEach(() => {
spyOn(app, 'secondFunc');
});
it("should call secondFunc spy", () => {
app.testFunc();
expect(app.secondFunc).toHaveBeenCalled();
});
});
The way you have it now, the spyOn is replacing the secondFunc property on your returned object with a proxy, but your code calls the secondFunc function that is inside the closure of the anonymous function. There are several ways to restructure your code to better expose the functions.
You could structure your module this way:
exports.testFunc = function() {
exports.secondFunc();
}
exports.secondFunc = function(){
console.log('not mocked!');
}
which is a lot smaller, easier to read, and let you mock the secondFunc function.
The reason this is happening is because you are setting up a mock on the returned object, but the code is calling the internal function. What I've done in the past is something like this:
module.exports = (function () {
function testFunc() {
api.secondFunc(); // Call the API function, which is what is mocked
}
function secondFunc(){
console.log('not mocked!');
}
var api = {
testFunc, secondFunc
};
return api;
})();

Expected spy but got function error message with Sinon stubs?

I have the following test suite:
describe('rendering Bundle View', function () {
beforeEach(function () {
this.view = new Backbone.View();
this.renderStub = Sinon.stub(this.view, 'render', function () {
this.el = document.createElement('div');
return this;
});
this.view.render();
});
it('should have called render once', function () {
console.info('RENDERRRR' + (typeof this.renderStub));
expect(this.renderStub.calledOnce).toBe(true); // this passes
expect(this.renderStub).toHaveBeenCalled(); // this fails
});
});
Why does the first expect statement pass but the second fail? The second gives the error message: expected Spy but got Function even though Sinon stubs implement the spy API so it should return a spy??
figured it out. I think its because I was using a Sinon spy with a jasmine function that expected a jasmine spy hence it didn't allow me to use Sinon expect statements

Resources