2

I'm trying to troubleshoot a unit test issue.

I used to have a mostly working Maven -> PhantomJS -> Qunit setup, but it was unpredictable so I took it apart to try to fix it.

I upgraded the software:

Qunit: 1.11.0
PhantomJS: 1.8
Phantom Qunit Runner Latest: https://github.com/jquery/qunit/tree/master/addons/phantomjs

I see the web GUI working. It runs and passes all 102 tests. The console prints this:

$ phantomjs --disk-cache=false runner.js http://localhost/ui/dcx/test.html
$ Took 16ms to run 0 tests. 0 passed, 0 failed.

If I comment out the exit command in the runner, it prints the console output for QUnit.done multiple times.

$ phantomjs --disk-cache=false runner.js http://localhost/ui/dcx/test.html
$ PhantomJS successfully loaded a page
$ QUnit.done callback fired
$ Took 15ms to run 0 tests. 0 passed, 0 failed.
$ QUnit.done callback fired
$ Took 1840ms to run 102 tests. 102 passed, 0 failed.
$ QUnit.done callback fired
$ Took 1841ms to run 102 tests. 102 passed, 0 failed.
$ QUnit.done callback fired
$ Took 1842ms to run 102 tests. 102 passed, 0 failed.
$ QUnit.done callback fired
$ Took 1848ms to run 102 tests. 102 passed, 0 failed.
$ ^C
$

Looks to me like the Qunit.done callback is getting executed too soon, then multiple times.

Anyone know why that callback fires?

My test inclusions and login delay might be relevant. I use AMD modules to define tests and curl.js to bring them in. Nothing happens until the security login does:

curl(['dolla'], function($){
        $.ajax({
        type: 'POST',
        url: '/svc/j_spring_security_check',
        data: {
            j_username: '7',
            j_password: '7'
        },
        success: function() {
            loadTests()
        }
    });
})
var loadTests = function () {
    curl([
        // Unit tests
         'dcx/dataControls/activity.test'
        , 'dcx/dataControls/eventList.test'
        , 'dcx/dataControls/mapViewer.view.test'
        , 'dcx/pages/deviceDetails.view.test'
        , 'dcx/pages/login.test'
        , 'dcx/pages/nodeProfiles.test'
        , 'dcx/pages/settings.view.test'
    ], function() {},  function(ex) { throw new Error(ex) })
})

EDIT: I'm down to a root cause, I think. If you include QUnit on a blank page, it calls QUnit.begin and QUnit.done right away. I need to delay execution of Qunit until after the security login is successful and curl has brought in my unit tests. Is there a way to delay the start of QUnit, but still keep the Qunit object available? I can't use stop() because there are many async tests that will call start().

SimplGy
  • 20,079
  • 15
  • 107
  • 144

3 Answers3

2

Found the answer. You can configure QUnit to not start, then start it manually when all your tests are loaded. This prevents the duplicate calling of Qunit.done which is the root cause of this issue.

http://forum.jquery.com/topic/are-qunit-and-requirejs-compatible#14737000001967123

SimplGy
  • 20,079
  • 15
  • 107
  • 144
0

This is one way to do it--modify the runner to not exit if there are not test results. https://gist.github.com/SimpleAsCouldBe/5059623

This doesn't work though--Qunit.done fires whenever the test stack is cleared. In an asynchronously loaded environment like Curl/Require.js, this can happen any time.

SimplGy
  • 20,079
  • 15
  • 107
  • 144
0

This is if you don't want to use require. For example, in a browser context maybe.

Having spent ages to find a method to turn loading of scripts (and stylesheets) into Promises (see here), I then found big problems with QUnit test suites starting to run before all these had loaded. Typically a handful of tests, at the start, would complain than a certain variable or class was undefined, although later tests wouldn't have that difficulty.

You can stop automatic starting by going like this:

QUnit.config.autostart = false;

... seemingly just putting it in one of several files will suffice.

To start the QUnit tests, then, you have to go QUnit.start();. But, understandably perhaps, you can't execute this from inside any code which is being run by a QUnit test. This gets complicated. In the end I did this in my app-starting code:

await this.loadInjectedFile( GLOBAL_SCRIPT );
await this.loadInjectedFile( DBFORM_SCRIPT );
await this.loadInjectedFile( UDV_SCRIPT );
await this.loadInjectedFile( REACTIVITY_SCRIPT );
console.log( '... injected files loaded' );
// to allow QUnit to start testing
window.QUnitGreenLight = true;

... strictly speaking a naughty thing to do (allowing test-related code to sneak into your app code). A more compartmentalised approach could probably be found.

Then, inline in the HTML file from where you launch your testing:

<script>
    const tryToStartTesting = function(){
        setTimeout( function(){
            if( window.QUnitGreenLight ){
                QUnit.start();
            }
            else {
                console.log( 'QUnit green light not yet given!' );
                tryToStartTesting();
            };
        }, 10 );
    };
    tryToStartTesting();
</script>

... in practice it seems to take maybe a few hundredths of a second before the green light is given.

A bit scrappy, perhaps, but it seems to work.

mike rodent
  • 14,126
  • 11
  • 103
  • 157