make javascript function callback-ish

2019-08-05 07:23发布

i'm having a javascript function using jquery:

my.namespace.loadSomeStuff = function() {

    $.get('/Services/loadStuff', function(c){
        $("#stuffDiv").html(c);
    });
};

and am trying to write unit tests for this.

now, since this function is not really returning anything and loading some stuff asynchronous using jquery's 'get' i'm having a hard time testing this.

essentially what i would like to do is call this function, wait until it returns something and then access the loaded elements to verify... something like:

my.namespace.loadSomeStuff(function(){
    alert('boo');
    assertSame('Login', $("#stuffDiv").find(......);
});

now, i created a in the template that calls the unit test, and it will load and display correctly, but the alert or the assert are never executed.

is there a way i can do this without adding a ton of extra code to every single function i have?

thanks t

2条回答
再贱就再见
2楼-- · 2019-08-05 08:21

You'd have to add an argument to the loadSomeStuff method to receive the callback function you called in your example code, and then deliberately call it:

my.namespace.loadSomeStuff= function(callback) {
    $.get('/Services/loadStuff', function(c){
        $("#stuffDiv").html(c);
        if (callback!==undefined)
            callback();
    });
};

I'm not sure it's worth making your code more complicated merely to support unit tests, though you could argue that adding callbacks to async functions is of general-purpose usefulness.

An alternative would be making the test use a crude timeout.

You could use ajaxSuccess as mentioned by Nick (maybe combined with a timeout, and/or also catching ajaxError?). But it strikes me this might be adding a bit too much implementation knowledge for a unit test. What if loadSomeStuff is changed in future to work without an XMLHttpRequest, or maybe only use it sometimes, when it's not cached? Then the unit test will break. Sure, you can update the test to match the implementation, but then that's not really a useful unit test, is it?

查看更多
Root(大扎)
3楼-- · 2019-08-05 08:23

You can use the global AJAX events for this, for example:

$(document).ajaxSuccess(function() {
  assertSame('Login', $("#stuffDiv").find(......));
});

ajaxSuccess would happen after each AJAX request, if you wanted an event that ran after all simultaneous requests finished, then ajaxStop is what you're after. Both of these handlers don't have to be attached to your AJAX methods directly, they can be done in the unit tests.

These are normal events (full list here), so you can .bind()/.unbind() as needed, for example when the unit test above is done:

$(document).ajaxSuccess(function() {
  assertSame('Login', $("#stuffDiv").find(......));
  $(this).unbind('ajaxSuccess'); //cleanup
});
查看更多
登录 后发表回答