views:

374

answers:

5

Hi all:

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.

+3  A: 

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.

James Black
A: 

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 :)

frunsi
Another detail to think twice about the design: Can you really always reproduce when this "adding" of a handler happens? In this example it means, will "myFunction" always be called on this or that page? You (or another developer) have to _interprete_ that this function will install a ready handler - not obvious! I see no value in adding ready handlers in "other-than-global" code.. Well, just _add_ the ready handler in global code, and move your global code to a central place. I am sure, this will make you question obsolete then. It looks like you found a major design flaw in the app...
frunsi
+2  A: 

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>
Adam A
@Adam A: thanks. I tried something similar but had no success. I'm not trying to prove document.ready works or not, but rather the code inside it. I'm having a very tough time just trying to invoke it from my unit test. I'm using JsTestDriver, and it doesn't appear to be loading any document.ready functions regardless of whether I put it under a named function or not. I tried delaying it, window.onload, just_call_it, etc... but its just not responding.
BeraCim
I tried out the code snippet I gave, and it seems to work. What it does is override jQuery's document.ready function, since the DOMLoaded event won't fire in your unit test, meaning document.ready will never run.The snippet makes document.ready independent from the DOMLoaded event.If we're in a post-DOMLoaded state (as determined by the postReady variable) then immediately call the function that was passed to document.ready. If we're in a pre-DOMLoaded state, then just ignore any functions that document.ready would have called (and make sure the function still does what it needs to).
Adam A
@Adam A: I tried running with the exact same code snippet, running the test in isolation, plus leaving only 1 document.ready function in my code(I have several of them). Unfortunately nothing happened (I had alert on first line and it didn't pop up at all). I can't figure out what I'm missing or what's wrong.
BeraCim
Edited my answer with what I tried. If I had to guess what your issue is, I'd say that maybe jquery is being included after the code snippet? I think that might cause .ready to be overwritten again with the original function call. Maybe you code do your unit testing against a jquery file that has the snippet included at the very end. That way it'll always overwrite the original implementation.I'll add my quick test example to this answer in a second.
Adam A
I wrote it up and got distracted...so "a second" become "24 hours". Apologies.
Adam A
@Adam A: thanks for the example. My source and test files are in different files since my setup involves using JsTestDriver. I'm thinking that it might be such setup that prevents me from testing code inside document.ready. What annoys me the most is that I havent been able to test/invoke *anything* in document.ready no matter what I tried, which is starting to lead me think that testing the code under such setup is impossible. I'm not sure whether there are any functions in Javascript/jQuery that can just magically call the document.ready functions?
BeraCim
No time to test it now, but $(document).trigger("ready") might work?
Adam A
@ Adam A: document.trigger didn't work, but the example given worked... only within the test itself. The document.ready in the source file was never invoked. Tried in isolation, but didn't work either.
BeraCim
Since the example works, I'd say try overwriting jquery.1.3.2.js (or whatever file you use) with a custom one that includes the overwrite of document.ready at the end of the file. that way it'll run every time jQuery is loaded. Replace the real jquery with this version when running unit tests (but of course you wouldn't want it getting into production...).
Adam A
+2  A: 

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.

MillsJROSS
A: 

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.

BeraCim