How can I replace a call argument of a function with Sinon? - javascript

I am using sinon to make my tests, and I faced a problem that I can't find a proper solution
lets say, for simplicity, that I want, when I call console.log(),that the proper console.log receives the argument that I want in my test.
I have tryied:
Sinon.replace( console, 'log', () => console.log('mytestparam'))
Sinon.stub( console, 'log').callsFake( console.log('mytestparam'))
But it creates some kind of circular dependency that crashes
I cant simply make a full stub of the function, because it contains code that I want to run, I only want to replace the argument used for the call.
I have researched the sinon docs but I can't find any function that allows to call the original function changing the args
Any idea?

Related

Node redis doesn't like function.apply()

I'm trying to wrap a redis client in node but I seem to be having some issues with calling node-redis functions with .apply().
This is causing issues when I tried to do it, which I was able to work around, but now async is calling its functions using .apply() as well which is now causing issues.
Below is a simplification of what I'm doing:
var client = redis.createClient( myOptions );
function set(){
// do other stuff
client.set.apply( null, arguments );
}
However, when I do this I'm getting the following error:
TypeError: Cannot read property 'send_command' of null
at RedisClient.(anonymous function).RedisClient.(anonymous function) (E:\sitesroot\0\node_modules\redis\lib\commands.js:45:24)
The code works perfectly when I pass in the arguments manually like so:
function set( key, value ){
// do stuff
client.set( key, value );
}
This approach won't work though for the likes of wrapping hgetall which has an unspecified number of arguments...
Any insight on what might be causing this?
As #Bergi pointed out in the comment above, you need to pass the correct context when you apply.
client.set.apply( client, arguments );

Jasmine Unit Tests Unexpected this Reference

I am trying to test a fairly simple JavaScript function in Jasmine, however the first statement is throwing an error for being undefined.
myClass.prototype.functiontoBeTested = function() {
var x = this.method()
...
}
The above throws an error in Jasmine as method is not a function and is undefined. The prototype is altered earlier to have this method, and out of curiosity I assigned this method to my test object in the spec itself as such:
myObject.method = function(){return mockResults;};
So I decided to log this to the console and instead of it being my object, I see Window {top: Window, location: Location, document: document, window: Window, external: Object…} which doesn't seem right. I've had this issue before with testing a function in Jasmine that used this but just changed the source code to refer to the object by name since the function was being assigned to something within the closure. I can't do that in this case, and I'm curious why this is referring to something unexpected (at least to me).
Edit: Some details on what the test case looks like as requested:
it("updates the control count", function(){
var mockResults = { ... };
myObject.method = function() {return mockResults;};
expect(myObject.method).not.toThrow();
});
Right now I'm just trying to get the method to execute to completion during the test. The function to be tested updates the text on some HTML components, I'll work on verifying those once I can get it to actually run. The method that is causing an error is the first line of the function, and is simply an accessor method for the object being called. In actual execution, var x = this.method() runs without issue. When testing in jasmine var x = this.method() throws an error because method() is undefined for this. Instead of this referring to the calling object, it is referring to the window. This doesn't happen live, but only during testing with Jasmine. This method is undefined even when I forcibly define it for the test object just prior to execution in the test as above. That's when I decided to log this to console in the source code and realized it isn't referring to what I would have expected it to refer to.
In JavaScript this for a method depends on the context it was called from. When you do a call myObject.method(), then method was called from the context of myObject, therefore this is myObject.
When you pass your function to Jasmine toThrow matcher, it calls it as it was passed (see source code):
try {
actual();
} catch (e) {
// ....
}
Here, actual() is a call of your method. It has no any specific context, so by default it will be called from window context.
Solution is to explicitly bind you method to myObject like the following:
expect(myObject.method.bind(myObject)).not.toThrow();
Credits to questions, you can find more details there:
Does Jasmine's toThrow matcher require the argument to be wrapped in an anonymous function?
How to write a test which expects an Error to be thrown in Jasmine?

How to stub the return value on a mocked method in SinonJS

I want to do something like the following:
sinon.mock(obj)
.expects('func')
.atLeast(1)
.withArgs(args)
.returns(somePredefinedReturnValue);
Where I expect everything up to and including withArgs, but then I need to stub the return value of the method so that when it returns it doesn't break the rest of the execution flow within the method under test.
The reason I'm doing this is because I found out that some of my REST endpoint tests will silently pass when they should really be failing if a stubbed method with a callback that has an assertion inside of it doesn't get called. I'm trying to verify that these callbacks are actually getting called so that my tests don't give false positives.
In the official docs
http://sinonjs.org/docs/#stubs
var stub = sinon.stub(object, "method", func);
You could pass a function argument that returns your desired value.
EDIT:
This has been removed from v3.0.0. Instead you should use
stub(obj, 'meth').callsFake(fn)

Unable to access attribute after fetch

I am testing some code in my web console (using coffescript)
#user = new Onethingaday.Models.User()
#user.url= "/users/#{current_user.get('nickname')}.json?id_type=nickname"
#user.fetch()
console.log(#user)
console.log(#user.get("facebook_id"))
The console.log(#user) line shows the following:
However, console.log(#user.get("facebook_id")) shows undefined.
I see that user has the facebook_id attribute. How do I retrieve the value of it?
This looks like a timing issue, albeit an odd one. fetch is asynchronous; you need to wait until its success callback is called before trying to access any attributes.
Try this:
#user.fetch success: =>
console.log(#user)
console.log(#user.get("facebook_id"))
It's confusing that the first console.log would show user.attributes.facebook_id existing and the second wouldn't, but console.log is itself asynchronous in Webkit's implementation, so what's going on is that the #user.get call is being resolved immediately (synchronously), whereas the object inspection in the first console.log is resolving later—after the fetch completed.
It looks like facebook_id is a property of the attributes property of the #user object. If that's the case, I'd think the following would work:
#user.attributes.facebook_id

How to mock JQuery with Jasmine?

How can I test that a certain JQuery selector has been executed with Jasmine? I'm trying to do the following:
spyOn($.fn, 'init').andCallThrough();
// my code
expect($.init).toHaveBeenCalled();
But after this call, $('div') returns Object { selector="div", context=document, NaN=div.spec, more...}, though it has to return (and $.fn.init('div') does return it): [div.jasmine_reporter, div.banner, div.logo, 4 more...]. This stuff naturally breaks the code since the JQuery object is no longer usable.
Example:
Say I want to test that a JQuery selector has been called, I write:
it('tests', function() {
spyOn($.fn, 'init').andCallThrough();
$('html');
expect($.init).toHaveBeenCalled();
});
This result it an error from Jasmine: Error: Expected a spy, but got undefined.. Then I set a breakpoint in FireBug on $('html') line and when I get there and try to watch, what the value of $('html') is, I get:
Object { selector="html", context=document, NaN=html, more...}
If I comment out spyOn, on that line $('html') evaluates to:
[html]
Which is what I expected to see with spyOn as well.
Well it looks like Jasmine does it's spy stuff by replacing the spied-on object with a wrapped version, and that seems to be messing up jQuery as a whole because (from the jQuery source code):
// The jQuery object is actually just the init constructor 'enhanced'
I'd suggest trying to spy on one of the functions that init uses, specifically "merge". If you look at the jQuery code, you'll see that any HTML=>DOM stuff ultimately gets returned through a merge call:
return jQuery.merge( this, selector );
(that's line 152 if you happen to be looking at the source of jQuery 1.5.1).
By spying on merge you should be able to test whatever you're testing, without inadvertently replacing the guts of jQuery.

Categories

Resources