Monitoring of a library with sinon - javascript

I have the following code to test using sinon:
var req = new MultiPartUpload({
client: client,
objectName: "/" + obj.func.destPath(),
stream: obj.outStream,
headers: headers
}, function (err, body) {
obj.debug('uploaded' + body);
});
I have to test the creation of this object. How can I do it? I have tried with:
var MultiPartUpload = require('knox-mpu');
var stub = sinon.createStubInstance(MultiPartUpload);
instance(obj, function () {
expect(stub).to.have.been.called;
done();
});
But it doesn't work as expected. Any suggestion? Thank you :)
EDIT:
instance is the istance of the object that creates the MultiPartUpload object. The problem is that the instance signature cannot be changed and that the MultiPartUpload library is required in the file where instance is created.
In short: I have to spy the MultiPartUpload library, and the problem is that is not possible to communicate in any way with istance, where the library is used.

From the docs:
Creates a new object with the given function as the protoype and stubs
all implemented functions. The given constructor function is not
invoked
This mean that sinon.createStubInstance(MultiPartUpload); will return a new stub with all prototype functions as stubs. I think you looking for a way to spy if the MultiPartUpload function was called, so one way could be to overwrite MultiPartUpload with the stub:
var MultiPartUpload = require('knox-mpu');
var stub = sinon.stub().returns(sinon.createStubInstance(MultiPartUpload));
MultiPartUpload = stub;
instance(obj, function () {
expect(stub).to.have.been.called;
done();
});

The only way to make it work that I found was this: instead of having:
var MultiPartUpload = require('knox-mpu');
In the instance code. I changed it to:
MultiPartUpload = require('know-mpu');
Then in the test-case I simply put:
MultiPartUpload = sinon.spy();
instance(obj, function () {
expect(MultiPartUpload).to.have.been.called;
done();
});
Any way to do it better than this? (I don't like global vars). Thanks :)

Have you looked into something like https://github.com/felixge/node-sandboxed-module ? When you require the instance module, you could use SandboxedModule to substitute a spy for knox-mpu.
Edit: I can't give a complete working example, because you haven't given us all your code. But sandboxed-module works something like this:
var SandboxedModule = require('sandboxed-module')
, MultiPartUploadSpy = sinon.spy()
, expect = chai.expect
, YourInstanceModule = SandboxedModule.require('your-instance-module', {
requires: {'knox-mpu': MultiPartUploadSpy}
})
instance(obj, function () {
expect(MultiPartUploadSpy).to.have.been.called;
done();
});

Related

Function chaining with function names from list [duplicate]

What is the equivalent code of window["functionName"](arguments) in NodeJS server-side?
If you need such a capability within a module, one hack is to store such module functions in variables within the module and then call them by accessing them from the module object properties. Example:
var x = { }; // better would be to have module create an object
x.f1 = function()
{
console.log('Call me as a string!');
}
Now, within the module, you can call it using the value from a string:
var funcstr = "f1";
x[funcstr]();
I am learning the ropes with Node myself, the above is probably all sorts of wrong :-). Perhaps a marginally better way to write this example would be (for the module m.js):
module.exports =
{
f1: function() { console.log("Call me from a string!"); },
f2: function(str1) { this[str1](); }
}
Now you can:
var m = require('m.js');
m.f2('f1');
Or even just:
var m = require('m.js');
m['f1']();
FWIW!
you're looking for global
Note, however, that in modules nothing is ever exposed to this level
1) If methods are in same js file
define all methods as properties of Handler:
var Handler={};
Handler.application_run = function (name) {
console.log(name)
}
Now call it like this
var somefunc = "application_run";
Handler[somefunc]('jerry codes');
Output: jerry codes
2) If you want to keep methods in a different js file
// Handler.js
module.exports={
application_run: function (name) {
console.log(name)
}
}
Use method defined in Handler.js in different.js:
// different.js
var methods = require('./Handler.js') // path to Handler.js
methods['application_run']('jerry codes')
Output: jerry codes
If you want to call a class level function using this then following is the solution and it worked for me
class Hello {
sayHello(name) {
console.log("Hello " + name)
}
callVariableMethod() {
let method_name = 'sayHello'
this[`${method_name}`]("Zeal Nagar!")
}
}
If You need it in module scope, You can use something like this
var module = require('moduleName');
module['functionName'](arguments);
Honestly, looking at all these answers they seem a bit too much work. I was playing around to look for other ways around this. You can use the eval() command to print a variable as text then call it as a function
I.e
let commands = ['add', 'remove', 'test'];
for (i in commands) {
if (commands[i] == command) {
var c = "proxy_"+command;
eval(c)(proxy);
}
}
eval(string)(arg1, arg2);
This example script would execute the function proxy_test(proxy)
You know, the OP's code inspired me to try this:
global.test = function(inVal){
console.log(inVal);
}
global['test']('3 is the value')
But now that I think about it, it's no better than #Ravi' s answer.
I use this for node, see if this approach works for you
var _ = require('lodash');
var fnA1 = require('functions/fnA1');
var fnA2 = require('functions/fnA2');
module.exports = {
run: function(fnName, options, callback) {
'use strict';
var nameSpace = fnName.toString().split('.');
// if function name contains namespace, resolve that first before calling
if (nameSpace.length > 1) {
var resolvedFnName = this;
_.forEach(nameSpace, function(name){
resolvedFnName = resolvedFnName[name];
});
resolvedFnName(options, callback);
} else {
this[fnName](options, callback);
}
},
fnA1: fnA1,
fnA2: fnA2
};
call this like
importVariable.run('fnA1.subfunction', data, function(err, result){
if (err) {return callback(err);}
return callback(null, result);
});
That is not specific to the window object. In JavaScript any property of the object can be accessed this way. For example,
var test = {
prop1 : true
};
console.log(test.prop1); // true
console.log(test["prop1"]); // also true
Read more here : https://developer.mozilla.org/en/JavaScript/Guide/Working_with_Objects

Jasmine mocking a constructor within a function

I get an error when running Jasmine tests: 'ReferenceError: JSZip is not defined'. Here's my controller:
$scope.makeZip = function() {
var zip = new JSZip();
zip.file('myPhoto.txt', 'Hello World);
return 'foo' + zip.generate();
};
And test:
it('should make a zip with correct name', function() {
var zipFileName = scope.makeZip();
expect(zipFileName).toMatch('foo');
});
My guess is that I need to mock JSZip constructor somehow: I tried inserting the following at the beginning of the test:
spyOn(window, 'JSZip').andReturn(null);
but then I get 'JSZip() method does not exist'.
I thought this is similar problem to Jasmine - How to spy on a function call within a function?, but I couldn't find a right way to fix the issue.
Any ideas? Thanks for all help!
JSZip is defined on the window. You should not have to pass it into the constructor. In your jasmine page (where you holster your specs), reference jszip.js. If you want, you could create a service..
your angular module.value("JSZip",JSZip);
then pass it into your controller. This is only useful if you wanted to mock JSZip, though.
Solved it! I didn't actually want to test JSZip, only to check whether a function makeZip is called correctly.
it('should make a zip with correct name', function() {
window.JSZip = function() {
this.file = function() {};
this.generate = function() {
return 'bar';
};
};
var zipFileName = scope.makeZip();
expect(zipFileName).toMatch('foo'); //makeZip should return string containing 'foo'
});

Spying on jQuery $('...') selector in jasmine

When it comes to spying on jQuery functions (e.g. bind, click, etc) it is easy:
spyOn($.fn, "bind");
The problem is when you want to spy on $('...') and return defined array of elements.
Things tried after reading other related answers on SO:
spyOn($.fn, "init").andReturn(elements); // works, but breaks stuff that uses jQuery selectors in afterEach(), etc
spyOn($.fn, "merge").andReturn(elements); // merge function doesn't seem to exist in jQuery 1.9.1
spyOn($.fn, "val").andReturn(elements); // function never gets called
So how do I do this? Or if the only way is to spy on init function how do I "remove" spy from function when I'm done so afterEach() routing doesn't break.
jQuery version is 1.9.1.
WORKAROUND:
The only way I could make it work so far (ugly):
realDollar = $;
try {
$ = jasmine.createSpy("dollar").andReturn(elements);
// test code and asserts go here
} finally {
$ = realDollar;
}
Normally, a spy exists for the lifetime of the spec. However, there's nothing special about destroying a spy. You just restore the original function reference and that's that.
Here's a handy little helper function (with a test case) that will clean up your workaround and make it more usable. Call the unspy method in your afterEach to restore the original reference.
function spyOn(obj, methodName) {
var original = obj[methodName];
var spy = jasmine.getEnv().spyOn(obj, methodName);
spy.unspy = function () {
if (original) {
obj[methodName] = original;
original = null;
}
};
return spy;
}
describe("unspy", function () {
it("removes the spy", function () {
var mockDiv = document.createElement("div");
var mockResult = $(mockDiv);
spyOn(window, "$").and.returnValue(mockResult);
expect($(document.body).get(0)).toBe(mockDiv);
$.unspy();
expect(jasmine.isSpy($)).toEqual(false);
expect($(document.body).get(0)).toBe(document.body);
});
});
As an alternative to the above (and for anyone else reading this), you could change the way you're approaching the problem. Instead of spying on the $ function, try extracting the original call to $ to its own method and spying on that instead.
// Original
myObj.doStuff = function () {
$("#someElement").css("color", "red");
};
// Becomes...
myObj.doStuff = function () {
this.getElements().css("color", "red");
};
myObj.getElements = function () {
return $("#someElement");
};
// Test case
it("does stuff", function () {
spyOn(myObj, "getElements").and.returnValue($(/* mock elements */));
// ...
});
By spying on the window itself you have access to any window properties.
As Jquery is one of these you can easily mock it as below and return the value you require.
spyOn(window, '$').and.returnValue(mockElement);
Or add a callFake with the input if it needs to be dynamic.

Stubbing a class method with Sinon.js

I am trying to stub a method using sinon.js but I get the following error:
Uncaught TypeError: Attempted to wrap undefined property sample_pressure as function
I also went to this question (Stubbing and/or mocking a class in sinon.js?) and copied and pasted the code but I get the same error.
Here is my code:
Sensor = (function() {
// A simple Sensor class
// Constructor
function Sensor(pressure) {
this.pressure = pressure;
}
Sensor.prototype.sample_pressure = function() {
return this.pressure;
};
return Sensor;
})();
// Doesn't work
var stub_sens = sinon.stub(Sensor, "sample_pressure").returns(0);
// Doesn't work
var stub_sens = sinon.stub(Sensor, "sample_pressure", function() {return 0});
// Never gets this far
console.log(stub_sens.sample_pressure());
Here is the jsFiddle (http://jsfiddle.net/pebreo/wyg5f/5/) for the above code, and the jsFiddle for the SO question that I mentioned (http://jsfiddle.net/pebreo/9mK5d/1/).
I made sure to include sinon in the External Resources in jsFiddle and even jQuery 1.9. What am I doing wrong?
Your code is attempting to stub a function on Sensor, but you have defined the function on Sensor.prototype.
sinon.stub(Sensor, "sample_pressure", function() {return 0})
is essentially the same as this:
Sensor["sample_pressure"] = function() {return 0};
but it is smart enough to see that Sensor["sample_pressure"] doesn't exist.
So what you would want to do is something like these:
// Stub the prototype's function so that there is a spy on any new instance
// of Sensor that is created. Kind of overkill.
sinon.stub(Sensor.prototype, "sample_pressure").returns(0);
var sensor = new Sensor();
console.log(sensor.sample_pressure());
or
// Stub the function on a single instance of 'Sensor'.
var sensor = new Sensor();
sinon.stub(sensor, "sample_pressure").returns(0);
console.log(sensor.sample_pressure());
or
// Create a whole fake instance of 'Sensor' with none of the class's logic.
var sensor = sinon.createStubInstance(Sensor);
console.log(sensor.sample_pressure());
The top answer is deprecated. You should now use:
sinon.stub(YourClass.prototype, 'myMethod').callsFake(() => {
return {}
})
Or for static methods:
sinon.stub(YourClass, 'myStaticMethod').callsFake(() => {
return {}
})
Or for simple cases just use returns:
sinon.stub(YourClass.prototype, 'myMethod').returns({})
sinon.stub(YourClass, 'myStaticMethod').returns({})
Or if you want to stub a method for an instance:
const yourClassInstance = new YourClass();
sinon.stub(yourClassInstance, 'myMethod').returns({})
I ran into the same error trying to mock a method of a CoffeeScript class using Sinon.
Given a class like this:
class MyClass
myMethod: ->
# do stuff ...
You can replace its method with a spy this way:
mySpy = sinon.spy(MyClass.prototype, "myMethod")
# ...
assert.ok(mySpy.called)
Just replace spy with stub or mock as needed.
Note that you'll need to replace assert.ok with whatever assertion your testing framework has.
Thanks to #loganfsmyth for the tip. I was able to get the stub to work on an Ember class method like this:
sinon.stub(Foo.prototype.constructor, 'find').returns([foo, foo]);
expect(Foo.find()).to.have.length(2)

Equivalent of "window["functionName"](arguments)" in server-side

What is the equivalent code of window["functionName"](arguments) in NodeJS server-side?
If you need such a capability within a module, one hack is to store such module functions in variables within the module and then call them by accessing them from the module object properties. Example:
var x = { }; // better would be to have module create an object
x.f1 = function()
{
console.log('Call me as a string!');
}
Now, within the module, you can call it using the value from a string:
var funcstr = "f1";
x[funcstr]();
I am learning the ropes with Node myself, the above is probably all sorts of wrong :-). Perhaps a marginally better way to write this example would be (for the module m.js):
module.exports =
{
f1: function() { console.log("Call me from a string!"); },
f2: function(str1) { this[str1](); }
}
Now you can:
var m = require('m.js');
m.f2('f1');
Or even just:
var m = require('m.js');
m['f1']();
FWIW!
you're looking for global
Note, however, that in modules nothing is ever exposed to this level
1) If methods are in same js file
define all methods as properties of Handler:
var Handler={};
Handler.application_run = function (name) {
console.log(name)
}
Now call it like this
var somefunc = "application_run";
Handler[somefunc]('jerry codes');
Output: jerry codes
2) If you want to keep methods in a different js file
// Handler.js
module.exports={
application_run: function (name) {
console.log(name)
}
}
Use method defined in Handler.js in different.js:
// different.js
var methods = require('./Handler.js') // path to Handler.js
methods['application_run']('jerry codes')
Output: jerry codes
If you want to call a class level function using this then following is the solution and it worked for me
class Hello {
sayHello(name) {
console.log("Hello " + name)
}
callVariableMethod() {
let method_name = 'sayHello'
this[`${method_name}`]("Zeal Nagar!")
}
}
If You need it in module scope, You can use something like this
var module = require('moduleName');
module['functionName'](arguments);
Honestly, looking at all these answers they seem a bit too much work. I was playing around to look for other ways around this. You can use the eval() command to print a variable as text then call it as a function
I.e
let commands = ['add', 'remove', 'test'];
for (i in commands) {
if (commands[i] == command) {
var c = "proxy_"+command;
eval(c)(proxy);
}
}
eval(string)(arg1, arg2);
This example script would execute the function proxy_test(proxy)
You know, the OP's code inspired me to try this:
global.test = function(inVal){
console.log(inVal);
}
global['test']('3 is the value')
But now that I think about it, it's no better than #Ravi' s answer.
I use this for node, see if this approach works for you
var _ = require('lodash');
var fnA1 = require('functions/fnA1');
var fnA2 = require('functions/fnA2');
module.exports = {
run: function(fnName, options, callback) {
'use strict';
var nameSpace = fnName.toString().split('.');
// if function name contains namespace, resolve that first before calling
if (nameSpace.length > 1) {
var resolvedFnName = this;
_.forEach(nameSpace, function(name){
resolvedFnName = resolvedFnName[name];
});
resolvedFnName(options, callback);
} else {
this[fnName](options, callback);
}
},
fnA1: fnA1,
fnA2: fnA2
};
call this like
importVariable.run('fnA1.subfunction', data, function(err, result){
if (err) {return callback(err);}
return callback(null, result);
});
That is not specific to the window object. In JavaScript any property of the object can be accessed this way. For example,
var test = {
prop1 : true
};
console.log(test.prop1); // true
console.log(test["prop1"]); // also true
Read more here : https://developer.mozilla.org/en/JavaScript/Guide/Working_with_Objects

Categories