Search code examples
javascriptjqueryqunit

QUnit test fails when document-ready block is declared inside the function being tested (IIFE)


I'm building a web widget with jQuery based on this post but with a few improvements: http://alexmarandon.com/articles/web_widget_jquery/

Now I started writing some tests but they are failing and I believe it has something to do with me declaring a document-ready block inside the function, as suggested in the link above.

Here is a stripped down version of the widget:

var MyWidget = (function(w, d, $, undefined){

    var _getContent = function () {
        return 'My Widget Content';
    }

    var render = function (){
        $(function(){
            var content = _getContent();
            $('#container').html(content);
        });
    }

    return {
        render: render
    }

})(window, document, jQuery, undefined);

Here is the test:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>MyWidget Tests</title>
    <link rel="stylesheet" href="../bower_components/qunit/qunit/qunit.css">
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture">
        <div id="container"></div>
    </div>

    <script src="../bower_components/jquery/dist/jquery.min.js"></script>
    <script src="../bower_components/qunit/qunit/qunit.js"></script>
    <script src="mywidget.js"></script>

    <script>
        QUnit.test('Test Content', function(assert){
            MyWidget.render();

            assert.equal($('#container').html(), 'My Widget Content');
        });
    </script>
</body>
</html>

And here is the result:

enter image description here

However, when I delete the $(function(){ code, then the test pass.

Any ideas what is happenning?

Thanks!


Solution

  • Okay, I figured out what the issue is. I should have seen it earlier. Even though your render function is being called after the document is ready (since the test calls it at the bottom), it is still asynchronous because of the ready handler you use ($(function() {}). As such, you will have to make your test asynchronous. Additionally, you will have to either (a) create some mechanism in your widget to notify the calling code that it is complete (like a Promise, or jQuery's Deferred); or (b) you will need to also use a ready handler in your test. Here is a test that works with your source code (and a JSBin to see it):

    QUnit.test('Test Content', function(assert){
      var done = assert.async();
      MyWidget.render();
      $(function() {
        assert.equal($('#container').html(), 'My Widget Content');
        done();
      });
    });
    

    If you wanted to go the jQuery Deferred route, you could do something like this in your render source:

    var render = function (){
        var defObj = jQuery.Deferred();
        $(function(){
            var content = _getContent();
            $('#container').html(content);
            defObj.resolve();
        });
        return defObj;
    }
    

    Then in your test you would essentially do what I have in the test example above, only with Promise handlers:

    QUnit.test('Test Content', function(assert){
      var done = assert.async();
      MyWidget.render()
        .then(function() {
          assert.equal($('#container').html(), 'My Widget Content');
          done();
        })
        .fail(done);
      });
    });