Sinon alert stub: "Attempted to wrap alert which is already wrapped" - javascript

I'm using Sinon 1.14. I would like to suppress all javascript alerts using Sinon's stubs.
Using the latest Chrome version: 42.0.2311.135 m, I get an exception: "Attempted to wrap alert which is already wrapped"
The code below works fine in latest Firefox. I will update with a fiddle.
var hooks = {
beforeEach: function(assert){
this.sandbox = sinon.sandbox.create();
this.sandbox.stub(window, 'alert', function (msg) { return false; });
},
afterEach: function(){
this.sandbox.restore();
}
};
module('example', hooks);
test('example', function(assert){
ok(true, 'does not throw an exception');
});

Updating Sinon from 1.14 to 1.14.1 seems to do the trick. I assume this was a bug?
As a side note, the code runs fine in 1.12 as well.

window.alert is a global function. Every time beforeEach runs it will replace that function with a wrapped function. I guess Sinon prevents you from wrapping a function twice.
You can either make sure to have a setup function that runs only once.
Or modify your code so that you don't have global dependencies (ie. pass in a reference to alert or a delegating window object). This can impact your code architecture a lot. This also shows why it is important to think about testing when designing your architecture.

Related

Passing context to Sinon stub's fake function

I'm using the mochawesome test reporter for my mocha tests. I'd like it to record my logging as well, and attach it to whatever test was running when the log was written. This can be achieved with addContext(). However, I also want the logs to output to the console, so I can monitor them as the test is running, rather than wait til it all finishes and the report is generated.
I've got 98% of a solution, based on this answer, but am struggling based on the fact that mochawesome's addContext() requires you to pass it a test object. Here's what I've got:
beforeEach(`Spy on logger`, function() {
const origLogInfo = logger.info.bind(logger),
origLogError = logger.error.bind(logger),
testContext = this
sinon.stub(logger, 'info').callsFake(function(message) {
addContext(testContext, `INFO: ${message}`)
origLogInfo.call(testContext, message)
})
sinon.stub(logger, 'error').callsFake(function(message) {
addContext(testContext, `ERROR: ${message}`)
origLogError.call(testContext, message)
})
})
afterEach(`Remove stubs`, function() {
logger.info.restore()
logger.error.restore()
})
it('counts approved hours', async function() {
logger.info(`Approving timesheets...`)
...
So before each it(), I get a reference to the original logger.info() and logger.error() functions, then I stub them. The stub calls a function which calls mochawesome's addContext() function, passing it a reference to the beforeEach() and whatever string I've sent to logger.info(). Then the original is called.
My problem is that every time addContext() is called, it attaches the logs to the beforeEach() hook. I can see why it's happening, too. Stepping through the code reveals that when beforeEach() is executing, this has two properties: currentTest, and test. The former refers to the it() which is about to be called, and the latter is the beforeEach() hook itself. This is great! If it stayed like this, addContext() would pick up currentTest and attach the logs to it (link to source).
By the time the argument to callsFake() is called, however, that same object has lost its currentTest property, so addContext() instead attaches the logs to the beforeEach() hook itself. Resulting in a test report with all the logs attached to the beforeEach hook instead of the relevant tests.
Is there any way I can write this so the callsFake() argument has a reference to the test that logger.info was called from? The following works, but requires extra code inside each it():
boundLoggerInfo = logger.info.bind(this);
boundLoggerInfo(`Approving timesheets...`)
Cheers! Hopefully I've provided enough info without being too verbose...
Update: I managed to work around the issue and achieve what I was after, after a good night's sleep:
testContext = {
currentTest: this.currentTest
}
Now a reference to currentTest is maintained even after beforeEach() loses it (still not sure why that happens).

Jasmine test: how to call through function that is called in tested function?

I try to test a JavaScript function with Jasmine. It looks like this:
show: function(){
this.work();
var mod = new Module();
mod.doSomething();
}
I am now testing if work() is called in show(), which is no problem. But then an error occurs in mod.doSomething(). This makes my test fail although it actually passed. What can I do to prevent mod.doSomething to be called or at least executed. Is there something like callThrough() for nonused function calls?
The only idea I had was to write a global variable that can be set true or false from everywhere. Then I extend the mod.doSomething() function for not to be called if this variable is set true. Now I can simply set the variable to true, if I do not want this function to be executed while testing. But I think there must be a better solution. Can anybody help me?
this is not testable with "standard" jasmine-practices. the method show() should take a module as parameter.... then it would be testable with a spy on doSomething()...

How is JavaScript's strict mode implemented

Update:
Perhaps the way the function is called is to blame, so here's to it:
2 JS files Main.js: self invoking (non-strict) function that adds an event listener for the '(on)load' event. The callback calls a loader function, that parses the location.pathname, and calls an init function, and detaches/removes the '(on)load' listener & returns null (explicitly).
PageSpecific.js: contains the _init function, adds a couple of event listeners to the body.
One of these listeners' callback (also returned from a closure) calls the strict function that uses argument.callee as a reference for recursion. The closure that returns the event handler may -depending on the browser- or may not bind and unbind other events, but I think that's irrelevant here, as this is to imitate an onchange event in IE <9
I hope this is reasonably clear, so its: anon. F => eventlistener => handler (named but declared in anon F) => pageloader => init => eventListener binding function returned by closure => calls strict function
Incidentally: Here's a trimmed down version of the _init function that is called, that I'm actually using. More specifically: the closure that binds the event Listener and - handler together. Its another one of my length questions, to which nobody seems to know the answer... hint ;-)
I'm debugging some fairly large (and complex) JavaScripts. In doing this, I noticed that I have a function, using strict mode that works fine but should, if I'm not mistaken, throw errors. Since the scripts are fairly sizeable and complex (event delegation, stacked closures etc), here's a simple example:
function withCalleeRecursion(foo)
{
'use strict';//strict throws typeError on arguments.callee
foo = foo.replace(/(a|b)+/gi, function (p1,p2)
{
if (p1.match(/(a|b){2,}/i))
{
return p1.replace(/(a|b)/gi,arguments.callee);//no errors
}
return (p2.match(/a/i) ? 'X':'Y');
});
return foo;
}
(function()
{//not strict
alert(withCalleeRecursion('Abba makes me barf'));
})();
In my actual script, this works perfectly fine. When I pasted this both in Firebug and chrome console, an error is thrown, though. I've tried this code here, so IE should throw errors, too, but when I run the code in IE's debugger, it works just fine. As far as I can work out, changing the doctype (tried html5 and html4) makes no difference.
Am I right in thinking that (most) browsers aren't as strict with the 'use strict'; directive as it's name suggests? It would seem that the browsers choose to ignore it when a possible error is detected when parsing the script. Is this true?
Meanwhile, I have made a slight change to the function, just out of precaution. Since I've seen quite a few questions here of people wondering how to get the callee reference in strict mode, I'm pasting it here, too:
function withCalleeRecursion(foo)
{
'use strict';
foo = foo.replace(/(a|b)+/gi, function abR(p1,p2)
{
if (p1.match(/(a|b){2,}/i))
{
return p1.replace(/(a|b)/gi,abR);
}
return (p2.match(/a/i) ? 'X':'Y');
});
return foo;
}
Name the callback, that's all.
It's probably because browser consoles use eval(), which changes things. Although putting "use strict"; at the start of a string of code that is passed to eval() works as expected, it's possible that console implementations prepend code to the string you've typed into the console, meaning that "use strict"; is no longer the first statement executed and is therefore ignored.
There's a reference to this and a suggested workaround in the following article:
http://javascriptweblog.wordpress.com/2011/05/03/javascript-strict-mode/
The suggested workaround is to wrap code in the console within a function that is immediately executed:
(function() {
"use strict";
nonExistentVariable = 1; // Error is now thrown
})();
Maybe this article can help you to understand more. Anyway the solution is the one you mention, the error is because access to arguments.caller and arguments.callee throw an exception in strict mode. Thus any anonymous functions that you want to reference will need to be named.

Unit testing with Jasmine: code in beforeEach() not seen by test's spyOn()

New to unit testing in general and Jasmine in particular.
I've set a variable in a beforeEach() callback, but it doesn't seem to work on the second test. It's supposed to fire initialization stuff in advance of every test within its context, right? I'm sure my spyOn() call is to blame, but I don't know how to fix it.
Comments explain the passes and fails:
describe("Test suite for my library", function () {
var html,
body,
play,
...
// custom matcher...
beforeEach(function () {
this.addMatchers({
toBeInstanceOf : function (constructr) {
return this.actual instanceof constructr;
});
});
});
describe("Within the Button object", function () {
beforeEach(function () {
play = new Button("play", false);
});
describe("play", function () {
// This test passes, as expected...
it("should be an instance of the Button object", function () {
expect(play).toBeInstanceOf(Button);
});
});
describe("play.name", function () {
// This test failed with the message
// "Expected spy Button to have been called
// with [ 'play', false ] but it was never called."
it("should be the first argument passed to the Button constructor", function () {
spyOn(window, "Button");
play = new Button("play", false); // ...until I added this line. Now it passes.
expect(window.Button).toHaveBeenCalledWith("play", false);
});
// This test passes, even if the one above fails.
it("should be 'play'", function () {
expect(play.name).toBe("play");
});
});
});
});
The documentation explains the usage, but not the context, of spyOn(), so I can't tell if I've created a bug or if I'm unknowingly taking advantage of a feature.
I can post the constructor if anyone thinks it makes any difference in the diagnosis, but I can assure you it's dead simple.
I'm sure it's a straightforward fix using some basic unit testing concept I'm having to learn the hard way. Thanks in advance.
P.S. I realize what I'm testing for in that failing spec isn't what I've described. I'm working my way through the API guide, looking for a way to get to the arguments array within a function call, so I can do a specific test on arguments[0]. Hints are appreciated, but not necessary. I'll figure it out.
Short answer: No, Before each and spies are not incompatible
You must Spy before you call if you want the spy to know about the call. You can use spyOn(object, 'function').andCallThrough() if you do not wish to interfere with its default behavior.
Long answer: The way faking/mocking/stubbing/spying frameworks often work is by replacing the method you are calling with a method that the mocking framework can control. Any calls to that function before it is replaced with the spy cannot be observed. This is a good thing, though mildly inconvenient,
Its cause you spy on window.Button after you have called. Im not totally sure what spy does, but after all it displaced the function you spy on with another function where it can check the function was called and whats arguments was passed. When you create your Button before your start your test, the original window.button function called. Then you replaces the function with the spy and test that the spy was called, so your test must fail.
Seems either create your Button in the test itself or create your spy before you call new Button in your beforeEach function.

How to invoke $(document).ready(function() {}) in unit testing

I'm experiencing difficulties trying to invoke document.ready( function() {}) in my unit tests. Suppose I have multiple of them in my javascript file, and one of them called inside a named function i.e.
function myFunction() {
$(document).ready(function() {
//...
});
}
How do I actually invoke them in my unit tests so I can actually test them? I'm using JsTestDriver to unit test my javascripts.
Thanks.
If it's a unit test, I'm guessing you check the function outputs when given certain inputs?
Here's my opinion:
You should prepare for the case where document.ready is called and the case where it isn't.
So your unit test should run each function twice - once to simulate a pre-ready call and one to simulate a post-ready call. That is, you should have one run-through where anything that happens on document.ready DOES run, and one run-through where it's just ignored (presumably to be called later on in the lifecycle).
EDIT:
Just reread the question and understood it a bit more. You could just override $(document).ready to do what you want it to (which is NOT to wait for the DOMLoaded event to fire, but instead to run the functions immediately). This snippet will replace the $(document).ready function with a function that does exactly that. It should run before any unit tests.
var postReady = true; // or false to ignore the function calls.
jQuery.fn.ready = function(fn)
{
if(postReady && fn) fn();
}
Example test case:
<html><head><title>whatever</title>
<script type="text/javascript" src="/JS/jquery-1.3.2.js"></script>
<script type="text/javascript">
var postReady = true; // or false to ignore the function calls.
jQuery.fn.ready = function(fn)
{
alert("We stole ready!");
if(postReady && fn) fn();
}
$(document).ready(function()
{
alert("The function is called.");
});
</script>
</head><body></body>
</html>
You know document.ready... works so just start with calling the functions within it. Ideally, if you just have an init function called by the ready function then you call one function, it does what you need, and you can continue with your tests.
You can take unit testing too far, in this case you need to ask yourself what you are testing, and why. The JQuery document.ready function works, and work well (you know this because it's been tested by many many people).
I would assume the trick would be to, instead of creating an anonymous function, naming one, and using it.
//So instead of this...
$(document).ready(function() {...});
//Do the following
$(document).ready(my_function);
Then you just test my_function and make sure that it is working. Make sure that you test the functions in the order their going to be loaded for an accurate test.
I suggest you to refactor the code. Even if you find a way to call it, it will be hard to understand for other developers.
Also (IMHO, I am not quite sure) you have to call the ready handlers even after the pages ready event was triggered, because if you "install" the ready() handler, if the document.ready event was already trigger, jquery calls that handler immediately (so it never loses that event, even if your code added a handler too late - that is, way after document.ready was still done).
Couldn't you just create a user my_on_read() event ? Or something the like?
Well, in the end, please just take care of ready() events and handlers that will be installed after the document.ready() is already done :)
Part of the answer to this question can be found here.
Below is the sample code to answer this question based on the above answer:
myFunction();
$.readyList[1]();
The index assumes that there is only 1 document.ready function in the source file. Index 0 refers to something else which I believe is info on the browser.

Categories