Skip to content


Subversion checkout URL

You can clone with
Download ZIP


setup() methods are called prematurely when tests are loaded asynchronously #320

wants to merge 1 commit into from

3 participants


I believe this might be an example of this problem in the wild.

If tests are loaded asynchronously, the document is loaded before they are read, which changes the values of config.autorun and config.blocking and eventually causes the callbacks queued in Test.queue() to be run out-of-sequence.

This pull request contains a separate test HTML file with accompanying JS tests that exemplify the problem.

The exact sequence of problematic events goes like this:

  1. DOM loads
  2. QUnit.load() completes, and calls QUnit.start()
  3. QUnit.start() sets config.blocking to false, then calls process()
  4. There's nothing in the queue, so process() calls done()
  5. done() sets config.autorun to true
  6. Test A queues init(), which is autorun by synchronize()
  7. Test A queues the inner run(), which is autorun
  8. Test A queues setup(), which is autorun
  9. Test A queues run(), which is autorun; now we're blocking.
  10. Test A queues teardown() and finish(). queue is now [A.teardown, A.finish]
  11. Test B queues init() and inner run(). queue is now [A.teardown, A.finish, B.init,]
  12. Test C queues init() and inner run(). queue is now [A.teardown, A.finish, B.init,, C.init,]
  13. Test A's teardown() and finish() are run; no more blocking. queue is now [B.init,, C.init,]
  14. Test B's init() and inner run() are run. queue is now [C.init,]
  15. Test B queues setup(). queue is now [C.init,, B.setup]
  16. synchronize() queues Test B's setup() and kicks off process()
  17. process() runs the next functions in the queue, which are Test C's init() and run().
  18. Test C queues setup(). queue is now [B.setup, C.setup]

And now you're thinking with portals.


nice job, good test though, i had the same idea with the array for the stack exchange post but yours is more elegant.


Usually what you need to do here is to set QUnit.config.autorun = false, then call QUnit.start() once you're done loading tests. Certainly needs (better) documentation, but I don't think there's much the framework could do.


There's an example for using QUnit.config.autostart in the API documentation now:

Beyond that I would need more input to address this, until then I've got to assume the above is a valid solution.

@jzaefferer jzaefferer closed this

thanks, that solved it. I'm glad that you have now a real api documentation.


@jzaefferer - How about adding QUnit.config.autostart to the file in my test? I don't think there's already a test for this functionality (is there?)


@mcantor good suggestion, thanks. Landed in 4e03a4b

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 20, 2012
  1. @mcantor
This page is out of date. Refresh to see the latest.
Showing with 53 additions and 0 deletions.
  1. +26 −0 test/async.html
  2. +27 −0 test/async_setup.js
26 test/async.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+ <meta charset="UTF-8" />
+ <title>QUnit Test Suite</title>
+ <link rel="stylesheet" href="../qunit/qunit.css">
+ <div id="qunit"></div>
+ <div id="qunit-fixture">test markup</div>
+ <script type="text/javascript" src="../qunit/qunit.js"></script>
+ <script type="text/javascript">
+ // Simulate a delay in retrieving the tests, as when they are loaded
+ // asynchronously using steal, etc.
+ // This results in the document loading before any tests are loaded,
+ // which means config.autorun is true and config.blocking is false.
+ var head= document.getElementsByTagName('head')[0];
+ var script= document.createElement('script');
+ script.type= 'text/javascript';
+ script.src= 'async_setup.js';
+ setTimeout(function() {
+ head.appendChild(script);
+ }, 1000);
+ </script>
27 test/async_setup.js
@@ -0,0 +1,27 @@
+var empty_me = ['start'];
+module("multiple async tests with setup", {
+ setup: function() {
+ empty_me = [];
+ }
+asyncTest("async A", function() {
+ equal(empty_me.length, 0, "setup should have emptied the array.");
+ empty_me.push('A');
+ start();
+asyncTest("async B", function() {
+ equal(empty_me.length, 0, "setup should have emptied the array.");
+ empty_me.push('B');
+ start();
+asyncTest("async C", function() {
+ equal(empty_me.length, 0, "setup should have emptied the array.");
+ empty_me.push('C');
+ start();
Something went wrong with that request. Please try again.