I'm new to jasmine. My Company is using jasmine 1.3.1
I am trying to test if a variable is defined in a method. I keep coming back with a failure.
objectParentName.accessoriesSlider = {
modifyJSON: function(obj) {
var contentLoadJSON = [];
for (var property in obj) {
objectParentName.accessoriesSlider.contentJSONTotal++
contentLoadJSON.push(obj[property]);
}
contentLoadJSON = objectParentName.accessoriesSlider.sortJSON(contentLoadJSON, 'dateAdded', false);
return contentLoadJSON;
}
}
Here is jasmine for that method.
describe('Test "modifyJSON" function', function () {
beforeEach(function() {
spyOn(objectParentName.accessoriesSlider, 'modifyJSON');
var contentLoadJSON = []; //forcibly add variable to see if that fixes issue
objectParentName.accessoriesSlider.modifyJSON();
});
it('is defined as "modifyJSON"', function () {
/**
* objectParentName.accessoriesSlider.modifyJSON should be defined.
*/
expect(objectParentName.accessoriesSlider.modifyJSON).toBeDefined();
});
it('and "modifyJSON" is a function', function () {
/**
* This should be a 'function'.
*/
expect(objectParentName.accessoriesSlider.modifyJSON).toBeFunction();
});
describe('Test "contentLoadJSON" variable', function () {
it('is defined', function () {
expect(contentLoadJSON).toBeDefined();
});
});
});
I am getting this error
ReferenceError: contentLoadJSON is not defined
at .<anonymous> (http://localhost:8234/spec/jasmine_accessories_slider.js:300:24)
at jasmine.Block.execute (http://localhost:8234/?spec=accessories_slider.js:1164:19)
at jasmine.Queue.next_ (http://localhost:8234/?spec=accessories_slider.js:2196:33)
at jasmine.Queue.start (http://localhost:8234/?spec=accessories_slider.js:2149:10)
at jasmine.Spec.execute (http://localhost:8234/?spec=accessories_slider.js:2476:16)
at jasmine.Queue.next_ (http://localhost:8234/?spec=accessories_slider.js:2196:33)
at jasmine.Queue.start (http://localhost:8234/?spec=accessories_slider.js:2149:10)
at jasmine.Suite.execute (http://localhost:8234/?spec=accessories_slider.js:2621:16)
at jasmine.Queue.next_ (http://localhost:8234/?spec=accessories_slider.js:2196:33)
at jasmine.Queue.start (http://localhost:8234/?spec=accessories_slider.js:2149:10)
So, I have no idea why I am getting an error here.
The first problem is that the variable contentLoadJSON is not defined in the scope of the function you are calling it from.
When you define a variable using var, it becomes a local variable, and is only accessible inside the scope of that function. In this case, the function it exists in is:
beforeEach(function() {
spyOn(objectParentName.accessoriesSlider, 'modifyJSON');
var contentLoadJSON = [];
objectParentName.accessoriesSlider.modifyJSON();
});
The function you are getting the error in is a different function() { ... } that isn't nested in beforeEach. It is therefore can't see the local variable you defined.
One way to rectify this is by defining the variable in the scope of your test suite, instead of in the scope of the beforeEach function:
describe('Test "modifyJSON" function', function () {
var contentLoadJSON; // Define it here
beforeEach(function() {
contentLoadJSON = []; // Assign it here
// ... more code here ...
}
// ... more code here ...
describe('Test "contentLoadJSON" variable', function () {
it('is defined', function () {
// Test contentLoadJSON here...
The second problem is that just because variables have the same name doesn't mean they are actually the same variable. They have to also be in the same scope for that to be true. var contentLoadJSON inside modifyJSON is actually a totally different variable than the var contentLoadJSON you are defining in beforeEach in your test suite.
When you call the function under test, you are not assigning its result to the variable you are trying to test. In fact, you are actually throwing away the result immediately after you call the function.
To fix this, change:
var contentLoadJSON = [];
objectParentName.accessoriesSlider.modifyJSON();
// Note that contentLoadJSON still equals [] at this point.
// The return of the function call is getting thrown away,
// because you don't assign it to a variable
To:
var contentLoadJSON = objectParentName.accessoriesSlider.modifyJSON();
The third problem is that expect(someVariable).toBeDefined(); might not do what you are expecting it to do. It doesn't check that the variable is "in scope". It checks that the value someVariable is not equal to the value undefined. Unless your function might sometimes return undefined, then this particular test isn't very important and will probably never fail.
It may be better to check that the result is "truthy" (expect(contentLoadJSON).toBeTruthy()). It might even make sense to just check that it is equal to the value you'd expect for the given data/input.
Related
I'm a javascript newbie and trying to understand how functions work. I found a similar question here but it doesn't really answer my question.
Taking the following piece of javascript as an example
var test = function(){
console.log("kick off");
var insideTest = "variable inside test";
var init = function(){
var insideInit ="variable inside init";
console.log("inside init");
}
return{
init:init
}
}
test().init();
The above code prints the following:
kick off
inside init
But if I remove
return{
init:init
}
it gives me an error saying
Uncaught TypeError: Cannot read property 'init' of undefined
Also, even though I'm calling init method using test().init() it doesn't print inside Init if the return statement is removed.
My question is why is it necessary to return init:init to execute init method.
EDIT:
To answer why my init function is inside the test() function here is the larger picture of what i'm trying to do.
var test = function() {
var init = function() {
var a = 0;
function1();
function2();
}
var function1() = function() {
//some code
}
var function1() = function() {
//some code
}
return {
init: init
}
}
Have added inline comments. Also the init inside test will have access to variable defined outside it(init) scope which is closure. test is returning an object to access it's inner function.This specific pattern is revealing module pattern
var test = function(){
console.log("kick off");
var insideTest = "variable inside test";
// Here init is a function which is private to test function
// Any function calling test will not have access to init unless it is 'exposed'
var init = function(){
var insideInit ="variable inside init";
console.log("inside init");
}
return{
init:init // exposing the private function
}
}
When you return, you're returning an Object with a single key of init, which you've assigned the "init" function that you defined within the test function. This allows you to return multiple functions if you'd like, so you can chain calls.
If you'd prefer a different way, you could just return the function without the curly braces, ie. return init;, and then assign the return of test() to a variable. var externalInitFnc = test();
Edit: Looking back it seems that you are fuzzy on the idea of scope in Javascript. When you defined init within the test function, it is only accessible within that function. Similar to how a private variable in a Java class is only available within that same class.
Background
If I have the following module:
this_module: {
foo: {};
set_this_foo: function () {
this.foo.boo = 'something';
return this.foo;
}
}
and use Rewire to import the private function and then unit test this function using Sinon.js:
var set_this_foo = app.__get__('this_module.set_this_foo');
var spy = sinon.spy(set_this_foo);
spy();
expect(spy).to.have.returned({boo: 'something'});
I get the error message:
TypeError: Cannot set property 'boo' of undefined
because this ends up having the value of the global object. I can fix this issue by defining a global variable named foo before running the test, but would prefer not to pollute the global namespace.
Question
Is there an (elegant) way to define the value of this in relation to spy()?
I would do this a bit differently:
var myModule = app.__get__('this_module');
var spy = sinon.spy(myModule, 'set_this_foo');
myModule.set_this_foo();
expect(spy).to.have.returned({ boo : 'something' });
Since you can also refer to the spy by using the original method name (besides just spy), calling that will make sure that it's called in the correct context.
I always thought that these callbacks had their own scope. What's going on here?
Eton_file_sync.prototype.add_file_listener = function(filename){
//use chokidar to make a watcher for the filename
var watcher = this.chokidar.watch(filename, {
ignored: /[\/\\]\./,
persistent: true
});
var some_variable = 1;
watcher.on('change', function(path) {
console.log ('File', path, 'has been changed');
console.log (some_variable);
});
};
when calling it by changing the file, why does the output of some_variable actually work?
File buffercont.asc has been changed
1
They do have their own scope. If you define the value of that variable from within the event handler callback, you'd only be defining the value inside of that scope, but it would not affect the parent scope.
var some_variable = 1;
console.log(some_variable); // prints "1"
var callback = function() {
var some_variable = 5;
console.log (some_variable); // prints "5"
};
callback();
console.log(some_variable); // prints "1"
Notice in the above sample that defining the variable inside the function did not change it outside the function. Each function carries with it a scope chain, the same scope chain that it is created within. You can always access variables higher up in the chain unless they've been overridden further down the chain like in the above example.
Shouldn't hello be printed in the console because of the return statement? The code is immediately invoked because of the () at the end so why isn't it printing?
var Module = (function () {
var privateMethod = function () {
// private
};
var someMethod = function () {
// public
console.log('hello');
};
var anotherMethod = function () {
// public
};
return {
someMethod: someMethod,
anotherMethod: anotherMethod
};
})();
return {
someMethod: someMethod, // just a function reference
anotherMethod: anotherMethod // again a function reference
};
So, you're not calling the function. You're just returning the function reference attached to a property of an object. Try to use comma operator here, which evaluates to the right most statement, whilst executing someMethod() function.
return {
someMethod: someMethod(), someMethod, // first getting called and someMethod ref is passed to the property
anotherMethod: anotherMethod
};
It's because of the var keyword on the top infront of Module.
f you do the following in the console:
var a = 5 // a is set to 5 yet undefined is shown in the console.
If you were to take off the var keyword:
a = 5 // a is again set to 5 yet 5 is shown in the console.
But in your real code you would want to use the var keyword.
So simply put it's just the console's way of outputting like that, idk why.
Enlighting more,
function itseld is object of Function (see https://msdn.microsoft.com/en-us/library/ie/x844tc74%28v=vs.94%29.aspx)
So Basically you are adding a reference to object of type Function, which is not IIFE.
In my Node.js app, I'm passing in variables to functions by using require like so:
console.log(require('../controllers/controller')(variable)); // undefined
However, when I don't pass in the variable, it logs as a function, like so:
console.log(require('../controllers/controller')); // [Function]
My controllers are defined like this:
var Controller = function (variable) {
this.variable = variable;
};
Controller.prototype.method = function (someInput, callback) {
// can access this.variable;
};
module.exports = Controller;
I also get this error:
TypeError: Object function (variable) {
this.variable = variable;
} has no method 'method'
Any idea as to where I'm going wrong here? I'm stuck at this step and not sure how to debug further.
require('../controllers/controller') is a function. When you use it without new keyword it does not return anything. But when you use new function() it acts like a constuctor of the object. So what you want to do is to use new keyword if you need an object to be returned with its prototype methods.
var Controller = require('../controllers/controller'),
controller = new Controller(variable);
this is an old thread, but I had this issue and the accepted answer didn't help me.
To create a module with a parameter, I use this code:
module.exports = function(pName) {
return {
test1: function() {
console.log('In test 1 '+pName);
},
test2: function() {
console.log('In test 2 '+pName);
}
};
};
And to call the module's functions:
var testModule = require('testModule')('David');
testModule.test1();
testModule.test2();