2

I'm using QUnit in conjunction with require.js for unit-testing a backbone.js application. All of the tests are asynchronous, using the asyncTest method.

I'm using setup and teardown to build and remove a fixture for each test. My problem is that although asyncTest seems to be blocking, setup() gets called for each test before they start, pretty much all setup() calls run at the same time. This solution doesn't seem to fix my problem. Below I have an example of how I set up the module and here's a link to a test repository that illustrates the problem

My question is: Am I doing something wrong or is this QUnit's normal behaviour?

module('Module', {
    setup: function(){
        console.log('setup');
    },
    teardown: function(){
        console.log('teardown');
    }
})

asyncTest('Test 1', function() {
    setTimeout(function(){
        equal(2, 2, 'The return should be 2.');
        start();
    }, 400);
});

asyncTest('Test 2', function() {
    setTimeout(function(){
        equal(1, 1, 'The return should be 1.');
        start();
    }, 400);
});
Community
  • 1
  • 1
Julian Krispel-Samsel
  • 7,512
  • 3
  • 33
  • 40
  • You should take a look at [Sinon](http://sinonjs.org). It's worth spending some time learning it, really helps with async testing. – gustavohenke Jun 12 '13 at 13:20
  • @gustavohenke which part of sinon are you referring to? I had a look at it but only for it's mocking, stubbing, spying stuff. – Julian Krispel-Samsel Jun 12 '13 at 13:24
  • when you need to deal with timing, [fake timers](http://sinonjs.org/docs/#clock) are handy :) – gustavohenke Jun 12 '13 at 13:27
  • Ah right. For brevity, I'm using setTimeout as a replacement for my AJAX callbacks really. So replacing setTimeout isn't the right solution I'm afraid. Considering that, have you got another idea? – Julian Krispel-Samsel Jun 12 '13 at 13:32
  • Sinon can also fake the XMLHttpRequest or even a full server with routes and custom headers/status codes. You'll never more need to trust your server for testing AJAX :) – gustavohenke Jun 12 '13 at 13:42
  • I'm doing an integration test at the moment actually. So I do need real data. – Julian Krispel-Samsel Jun 12 '13 at 13:52
  • The code sample included in the question is misleading, though the linked GitHub repo is useful. Would be good to include more relevant code in the question though. – Jörn Zaefferer Jun 17 '13 at 10:43

2 Answers2

6

QUnit expects a rather specific page load behaviour. Loading it via requirejs might look like it works, but actually doesn't. The recommendation is to load QUnit via a regular script tag. Loading the tests itself via requirejs is fine, as long as you set QUnit.config.autostart = false, as you do.

Jörn Zaefferer
  • 5,665
  • 3
  • 30
  • 34
  • Thanks for that. Is that documented somewhere? – Julian Krispel-Samsel Jun 17 '13 at 11:48
  • It would be great if it were! – Julian Krispel-Samsel Jun 17 '13 at 13:19
  • I got the same problem when using qUnit with requireJS : I tried everything I could but it seems not to be possible to load it this way. Loading it with a script tag and loading the test files with requireJS does work. Note that the "QUnit.config.autostart = false" must be called before QUnit is initiated, so putting it in a file loaded with requireJS does not work. – personne3000 Nov 06 '13 at 10:31
0

Blogpost including the solution Jörn Zaefferer specifies above - http://www.jonnyreeves.co.uk/2012/qunit-and-requirejs/

here's what I implemented-

Problem I was having:

I included qunit via require. This seemed to work fine for all synchronous tests. I was defining a variable in my qunit module's setup and back to null on teardown. When I included an asyncTest, it seemed to not be reset properly by the teardown and was thus breaking all the tests that ran after that one that expected a fresh variable.

Key points

  • Include qunit via its own script tag
  • set autostart to false
  • start again after all test modules are required

It's probably not the most elegant, but I also didn't run across any end to end examples, and I just burned a couple hours finding this answer (since I first thought that I was setting my asyncTest up incorrectly).

<link rel="stylesheet" href="qunit-1.12.0.css">
<script type="text/javascript" src="qunit-1.12.0.js"></script>
<script>
    QUnit.config.autostart = false
    require(['App'],
    function(){

        //Modules that have been loaded in by require.js
        var loadedModules = [];

        //Locations of test modules to load
        var moduleLocations = [
            'tests/test1',
            'tests/test2',
            'tests/test3'
        ];

        //If all modules have been loaded, run them
        function runTests(){
            if (loadedModules.length == moduleLocations.length){
                QUnit.start();
                _.each(loadedModules,function(test){
                    test();
                });
            }
        }

        _.each(moduleLocations,function(string){
            require([string],
            function(a){
                loadedModules.push(a);
                runTests();
            });
        });

    });
</script>

Separate Test Module Files:

define(['array','of','test','requirements'], function() {

    return function(){

        var options = null;

        module('Autocomplete',{
            setup: function() {
                // prepare something for all following tests
                options = new whatever();
            },
            teardown: function() {
                // clean up after each test
                options = null;
            }
        });

        test( "normal tests", function() {
            ok(true,"normal assertions");
        });
    }
});
Community
  • 1
  • 1
iabw
  • 1,108
  • 1
  • 9
  • 24