How to spy on localStorage methods with jasmine - javascript

Suppose I have a JavaScript code:
function modifiesLocalStorage() {
var someBoolean = false;
if(localStorage.getItem('someKey') === 'true'){
localStorage.removeItem('someKey');
someBoolean = true;
}
return someBoolean;
}
Then I have a jasmine test to test this method:
it('should return true', function(){
spyOn(localStorage, 'removeItem');
spyOn(localStorage, 'getItem').and.returnValue('true');
var returnValue = modifiesLocalStorage();
expect(localStorage.getItem).toHaveBeenCalled(); //Error in this line
expect(returnValue).toBeTruthy();
});
while executing this test I am getting following error:
Error: <toHaveBeenCalled> : Expected a spy, but got Function.
What is this error and how do I fix it?
I am using Firefox 45.9.0 browser in headless mode to run the tests.

As per this question's answer:
Expected a spy, but got Function
We need to get into the actual method, which in this case is on the proto.
if I modify my tests like below, the test passes:
it('should return true', function(){
spyOn(localStorage.__proto__, 'removeItem');
spyOn(localStorage.__proto__, 'getItem').and.returnValue('true');
var returnValue = modifiesLocalStorage();
expect(localStorage.__proto__.getItem).toHaveBeenCalled();
expect(returnValue).toBeTruthy();
});
Since __proto__ is deprecated we can also use Object.getPrototypeOf(localStorage) to get the prototype of the localStorage object

Related

TypeError: undefined is not an object when unit testing with jasmine

I'm trying to write a unit test for a function and I'm getting an error. I'm also unsure how to test other parts of the function correctly.
private dictionaryMap (loggedIn, response) {
const translations = this.convertToArrays(response.data.translations);
this.configureMomentLocale(language);
if (!loggedIn) {
this.cachePublicDictionary(translations);
}
// not testing this part
this.dictionary = new Dictionary({
translationMap: Object.assign({}, this.getPublicDictionaryFromCache() || {}, translations),
});
return this.rx.Observable.of(this.dictionary);
}
And my unit test so far looks like this:
describe('dictionaryMap', () => {
it('calls configureMomentLocale()', () => {
const foo = {
'foo':'bar',
};
spyOn(service, 'configureMomentLocale');
service.dictionaryMap({}, false);
expect(service.configureMomentLocale).toHaveBeenCalled();
});
});
And when I run this test I get this error:
TypeError: undefined is not an object (evaluating 'response.data.translationMap')
Do I need to mock response.data.translations or assign the json structure? (translationMap: {'email': 'email', 'forgotPassword': 'Forgot password?'})
Also, I'm not sure how to properly test the other parts of the function, like the if statement or returning the observable. I am new to unit testing.
Your method dictionaryMap accepts 2 parameters - 1st is loggedIn (presumably boolean) and the 2nd one is response. On the first line of that method (before calling configureMomentLocale) you have a line const translations = this.convertToArrays(response.data.translations); which expects the response variable to have a property named data.
In your test, you have 2 errors on the line service.dictionaryMap({}, false);:
You're setting the arguments in reverse order - you should put the boolean argument first and the object one second
The object doesn't have a property named data
The line should be corrected to be something similar to service.dictionaryMap(false, { data: {} });. You might even need to define translations property for data object - it really depends on what this.convertToArrays function does and how it handles undefined values.

Test individual line in javascript unit testing

I have a javascript function like this
function formatInput(input) {
//want to test only this immediate statement
var type = input.ipType.toString().toLowerCase().trim();
var afterVormat = someFunction(type);
return afterFormat;
}
I am able to test this function(value of afterFormat) correctly , but is it possible/how to test a specific line in function since I am not returning type.
For example I want to test if var type is as it is expected
Is it possible/how to test a specific line in function?
The immediate answer: no.
The solution
One of the outcomes of adhering to TDD is that it forces you to build code in isolated, testable blocks. This is a direct consequence of the fact that you cannot perform test(s) of the individual lines of a function. In your case the solution is to restructure your code to:
var type = function(){
return input.ipType.toString().toLowercase().trim();
};
function formatInput(input) {
var type2 = type();
var afterVormat = someFunction(type);
return afterFormat;
}
Now you have made type an isolated block that you can test.
If you combine this with use of Sinon.JS you can use a spy to test that an invocation of function formatInput() will also result in the invocation of type() and thereby you know for sure that var type2 has been assigned the intended value.
I’m not aware of any specific and more advanced unit testing method/system for javascript, but you can have a simple assertion function to test individual lines of code for debugging purpose like this:
function assert(condition, message) {
if (!condition) {
message = message || "Assertion failed";
if (typeof Error !== "undefined") {
throw new Error(message);
}
throw message; // Fallback
}
}
(Code taken from TJ Crowder's answer to another question.)
Then you can just use it to check for instance the var type like this:
assert(type == "something expected here and shall throw an error otherwise");
You can use console.log() function for that. As below.
function formatInput(input) {
var type = input.ipType.toString().toLowerCase().trim();
console.log(type);
var afterVormat = someFunction(type);
return afterFormat;
}
Also you can use debugger; also, to debug the code line by line.
function formatInput(input) {
var type = input.ipType.toString().toLowerCase().trim();
debugger;
var afterVormat = someFunction(type);
return afterFormat;
}
and just press F10 key to debug the code and you can check the values in console.

Mocking ngResource in Angular unit tests

I have an ngResourceMockFactory which looks like this:
(function() {
'use strict';
angular.module('app')
.factory('NgResourceMock', ngResourceMockFactory)
;
ngResourceMockFactory.$inject = [];
function ngResourceMockFactory() {
function NgResourceMock() {
var context = this;
context.$promise.then = function() {
context.prototype.$promise.then.apply(context, arguments);
};
context.$promise.finally = function() {
context.prototype.$promise.finally.apply(context, arguments);
};
}
NgResourceMock.prototype.$promise = {
then: function(onSuccess, onError) {
this.$promise.onSuccess = onSuccess;
this.$promise.onError = onError;
},
finally: function(onComplete) {
this.$promise.onComplete = onComplete;
}
};
return NgResourceMock;
}
})();
I inject this into my tests in a beforeEach like so:
beforeEach(inject(function(NgResourceMock) {
ngResourceMock = new NgResourceMock();
}));
then I use it like this:
describe('initiateWorkflow function', function() {
beforeEach(function() {
vm.player = {id: 123};
spyOn(dataService, 'initiateWorkflow').and.returnValue(ngResourceMock);
vm.initiateWorkflow();
});
it('should call dataService.initiateWorkflow', function() {
expect(dataService.initiateWorkflow).toHaveBeenCalledWith({playerId: vm.player.id}, {});
});
});
but I keep seeing the following error:
TypeError: 'undefined' is not an object (evaluating 'context.prototype.$promise')
This leads me to believe that something is wrong with my ngResourceMockFactory, but I'm not sure what it is.
Don't know if this can be of any help, but if you are trying to evaluate asynchronous operations in your tests, you may want to use the done() method in Jasmine.
As per their documentation:
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
by passing done as a parameter of the beforeEach callback, any test run after the before each will wait until the done() function has been called.
Source: Jasmine (Asynchronous Support section).
Hope this helps.
Here is the solution to your problem.
The error TypeError: 'undefined' is not an object (evaluating 'context.prototype.$promise') is caused when you try to invoke the promise object before invoking the function into which it is defined or into which your parent function is defined.
Here the returnValue(ngResourceMock) is directly calling into the function without the context and parameters need to be defined.
Therefore you can try to add another beforeEach statement like
beforeEach(angular.mock.module(app));
to load your app module
Here may be the same concept related to your problem another link here.
Hope it may help you a bit.

confusion about velocity's beforeEach setUp function in meteor

I'm trying to implement testing on my meteor application using velocity [1] and jasmine [2].
I defined a collection called 'object' (collections/object.js):
Objects = new Meteor.Collection('objects');
I implemented a set up function (tests/jasmine/server/unit/ObjectSpec.js):
describe('Objects', function () {
'use strict';
// set up
beforeEach(function () {
// 1 - backup all data
MeteorStubs.install();
// 2 - delete current 'Objects'-items
Objects.remove({});
// 3 - add test data
Objects.insert({
someProperty: 'data1'
});
});
then I do run the actual tests (same file):
// actual tests
it("should delete a specific object", function () {
var selector = {someProperty: 'data1'};
var selectedObject = Objects.findOne(selector);
// will return 'Expected undefined not to be undefined.'
expect(selectedObject).not.toBe(undefined);
Meteor.call('deleteObject', selectedObject, function(error, id) {
if (error)
return alert(error.reason);
});
expect(Objects.findOne(selector)).toBe(undefined);
});
after, I restore the old application state in the tear down (same file):
// tear down
afterEach(function () {
Objects.remove({});
MeteorStubs.uninstall();
});
Now, when executing the test, velocity throws me:
Expected undefined not to be undefined.
I wonder, if the data in the 'beforeEach'-Function actually will be inserted and accessible in the actual testing function? Also, I tried using console.log() in the test functions, to show the current data, but it won't be shown in the browser console. Why?
[1] https://atmospherejs.com/velocity/html-reporter
[2] https://atmospherejs.com/sanjo/jasmine

Integrating sinonjs with intern testing

I can get tests to run in Intern, but I am struggling with getting spies to work. I'm trying to integrate sinon so I can get spies. Here is a sample test file:
define([
'intern!bdd',
//'intern/chai!expect',
//'intern/order!node_modules/intern/chai',
// 'intern/order!node_modules/chai/lib/chai',
// 'intern/order!node_modules/sinon/lib/sinon',
// 'intern/order!node_modules/sinon-chai/lib/sinon-chai',
'intern/order!node_modules/sinon/lib/sinon',
'intern/order!node_modules/sinon/lib/sinon/spy',
'intern/order!node_modules/sinon/lib/sinon/call',
'intern/order!node_modules/sinon/lib/sinon/behavior',
'intern/order!node_modules/sinon/lib/sinon/stub',
'intern/order!node_modules/sinon/lib/sinon/mock',
'intern/order!node_modules/sinon/lib/sinon/collection',
'intern/order!node_modules/sinon/lib/sinon/assert',
'intern/order!node_modules/sinon/lib/sinon/sandbox',
'intern/order!node_modules/sinon/lib/sinon/test',
'intern/order!node_modules/sinon/lib/sinon/test_case',
'intern/order!node_modules/sinon/lib/sinon/match',
'intern/order!vendor/src/angular/angular',
'intern/order!vendor/src/angular-mocks/angular-mocks',
'intern/order!src/common/modules/error_handling/error_handling'
], function (bdd, sinon, spy, call, behavior, stub, mock, collection, assert, sandbox, test, test_case, match) {
with (bdd) {
sinon.spy = spy;
sinon.call = call;
sinon.behavior = behavior;
sinon.stub = stub;
sinon.mock = mock;
sinon.collection = collection;
sinon.assert = assert;
sinon.sandbox = sandbox;
sinon.test = test;
sinon.test_case = test_case;
sinon.match = match;
describe('Error handler module', function () {
var test, scope, ctrl, error_handler, log;
function inject (fn) {
return function() {
angular.injector(['ng', 'ngMock', 'error_handling']).invoke(fn);
}
}
beforeEach(inject(function($log){
log = $log;
}));
it('should be an object', function(){
//expect(log).to.be.an('object');
});
it('should call console.trace with the string test', function(){
var spy = sinon.spy(console.trace);
//expect(sinon.spy).to.be.ok;
//log.debug('test');
console.trace('test');
//spy.should.be.ok;
spy.should.have.been.calledWith('test');
//chai.expect(spy).to.have.been.called.with('test');
});
});
}
});
I based it off of https://github.com/theintern/intern/blob/sinon/sinon.js
But I get this error failure:
>> 1/6 tests failed
Warning: FAIL: main - Error handler module - should log nothing when the logging mode is off (1ms)
TypeError: 'undefined' is not an object (evaluating 'spy.should.have')
at </Users/evanvandegriff/Documents/work/nomi_v2/nomi_v2/web/src/common/modules/error_handling/error_handling.test.js:67>
at <__intern/lib/Test.js:169>
at <__intern/lib/Suite.js:237>
at <__intern/node_modules/dojo/Deferred.js:37>
at <__intern/node_modules/dojo/Deferred.js:258>
at runTest <__intern/lib/Suite.js:241>
at <__intern/lib/Suite.js:249
Anyone have any ideas why this isn't working?
As #Fordio indicated, the syntax you're trying to use is part of Sinon-Chai, not vanilla Sinon.js. You'll need to require that package as a dependency, or use the native Sinon.js assertions.

Categories