After a good deal of hacking and a push from jzaefferer, I gotten the example code in QUnit-CLI to run using Rhino and no browser in sight. This isn’t a complete substitute for in-browser testing, but makes integration with build servers and faster feedback possible.

The first hurdle was adding guards around all QUnit.js’s references to setTimeout, setInterval, and other browser/document specific objects. In addition I extended the test.js browser checks to include all of the asynchronous tests and fixture tests. Finally I cleaned up a bit of the jsDump code to work better with varying object call chains. My alterations can be found on my fork here.

The second hurdle was getting QUnit-CLI using my modified version of QUnit.js and adjusting how Rhino errors are handled. Adding a QUnit submodule to the QUnit-CLI git repository easily fixed the first (I previously posted my notes on git submodules and fixed branches). QUnit.js’s borrowed jsDump code is used to “pretty-print” objects in test messages. jzaefferer ran into an issue when running QUnit’s own tests through QUnit-CLI resulting in the cryptic error:

js: "../qunit/qunit.js", line 1021: Java class "[B" has no public instance field or method named "setInterval".
at ../qunit/qunit.js:1021
at ../qunit/qunit.js:1002
at ../qunit/qunit.js:1085
at ../qunit/qunit.js:1085
at ../qunit/qunit.js:1085
at ../qunit/qunit.js:110
at ../qunit/qunit.js:712 (process)
at ../qunit/qunit.js:304
at suite.js:84

It turns out that errors objects (e.g. ReferenceError) throw in Rhino include an additional property of rhinoException which points to the underlying Java exception that was actually thrown. The error we saw is generated when the jsDump code walks the error object tree down to a byte array off of the exception. Property requests executed against this byte array throw the Java error above, even if they are done part of a typeof check, e.g.

var property_exists = (typeof obj.property !== 'undefined');

Once I figured this out, I wrapped the object parser inside QUnit.jsDump to properly pretty-print error objects and delegate to the original code for any other type of object.

With these changes we have a decent command line executable test suite runner for QUnit. With a bit more work QUnit-CLI will hopefully be able to print Ant/JUnit style XML output and/or include stack traces when errors bubble out of test code.

While working on getting QUnit-CLI cleaned up and refactored a bit, I realized I needed to tie the example code in the Git repository to a particular version of QUnit.js (those guys are making changes too fast for me to keep up). I have used SVN:externals prevsiously so Git submodules seemed like an obvious solution. A single submodule should allow me to keep QUnit-CLI inherently pointing to a particular revision of QUnit.js without requiring me to seperately document which version I was testing against.

The man page for git-submodule as well as the Git Book chapter on Submodules do a good job of documenting the command with some simple examples, but none that were 100% clear for my needs. In addition, I need my Git submodule to point to a specific commit (or branch) so that everyone cloning my code consistently can run my examples w/o fear that a new commit on HEAD will break something.

Step 1 : Add the submodule

Once the module is checked out, I need to add the QUnit submodule. First grab the GitHub url for my QUnit fork (eventually this will be replaced with the main QUnit repo) and execute the “add” command from within your local repository root.

git submodule add git://github.com/asynchrony/qunit.git qunit

Afterward there will be two modified and staged objects in your repo: .gitmodules will contain the submodule’s local path and source URL and a new folder named qunit which contains a full clone of your source repository.

** Fraser Speirs has a good writeup on what is going on behind the scenes with the Git internals and how the key to all of this is in the index files of each repo and the modes the changes are committed with. **

Step 2 : Fix the submodule to a particular commit

By default the new submodule will be tracking HEAD of the master branch but will NOT be updated as you update your primary repo. In order to change the submodule to track a particular commit or different branch change directory to the submodule folder and switch branches just like you would in a normal repo.

git checkout -b dev_branch origin/dev_branch

Now the submodule is fixed on the development branch instead of HEAD of master. Just easily I could set it to specific commit or tag.

Step 3 : Commit everything

Now from your primary repository you still have two modified objects: .gitmodules file and qunit folder. Commiting these changes will persist the new submodule tracking your desired branch.

Step 4 : Clone Recursive

The next time you (or someone else) clones this repo, they will need to do one of two things.

This is mostly a note on how I work with git. A lot of people used to subversion wonder why I’m so enamored of Linus Torvald’s latest project and I usually only explain enough of it to convince them that git is an elaborate and complicated tool for replicating a subversion workflow. This is a good example of a situation in which svn would fail you.

I try as much as possible to create a new branch every time I decide to work on a new unit of work. Oftentimes, I will just absent-mindedly start coding and be 4 or 5 commits deep before realizing that I forgot to branch (which is a situation git has tools to fix as well), but for this particular stretch of coding I was on a clean branch without any untracked files.

Shortly after starting, I found myself with 4 classes, none of which compiled. In a minimalist vein, at home I work with only emacs and ant when hacking on Java code. ant can cause a bit of scroll blindness when your code has compile errors, and the situation is only exacerbated when you don’t quite get Java generics.

One class was named WinLogicFactory, and it’s functionality was the end goal of this branch. The other 3 classes were support classes. They had resuable, general-purpose functionality that WinLogicFactory would use, but contained nothing specifically related to the application I was writing. I stole the idea of them from Haskell and the classes were named Either, Left and Right.

Before Either, Left and Right were fixed and compiling, I couldn’t fix WinLogicFactory, which would use them. A bit counter-intuitively I added WinLogicFactory to the index. This allowed me to stash my current index. That left only the generic classes in my working tree:

Once I got Either, Left and Right compiling I could now start working on WinLogicFactory, so git stash apply restored the index with my broken factory in it. I had some more work to do to get it compiling and just to convince myself that I had really gotten my workspace back to it’s original state, so I ran a git reset, which effectively pulled any uncommitted changes out of staging in the index.