QUnit Acceptance Test Framework with iFrame, Require JS, and jQuery Simulate

Our development team had a difficult time setting up an acceptance test framework for automated testing on headless browsers. We explored several options and ended up starting with HTML Unit. It worked great until we started using jQuery Mobile, and the Rhino javascript engine didn’t seem to render the page properly. We’re now using QUnit with iFrames, and although we still run into issues, it seems to work well in basic scenarios. I want to thank Matt Evanoff for posting a blog on this subject, which helped us through the initial set up of the tests. See Matt Evanoff’s Blog on QUnit.

I checked in the basic structure on Git Hub. The code is untested (I extracted it from our current project), but we’ll go over the code in this blog. See QUnit Acceptance Test Framework Repository on Git Hub

The QUnit Test Page
GitHub – index.html

As many developers are already familiar with, QUnit runs the tests through a browser from a HTML page. We have a QUnit test page for every web application, which we separate by directories for scalability. On Git Hub, I named this directory “app,” but it doesn’t have to be separated if there is only one application.

index

This index file loads QUnit with lines 4 (stylesheet), 6 (javascript), and 13 (div). It pauses QUnit though with line 16 (QUnit.config.autostart=false). We want to make sure QUnit starts after all the necessary resources are loaded.

The index file contains an iframe with id “myFrame,” which is where we will load our web application for testing.

All of our test scripts are loaded using Require JS. We use this library mainly for organization. Line 7 is using Require JS to load our main configuration file config.js.

The Main Configuration Page
GitHub – config.js

config

The main configuration page is for Require JS. It’s loading jQuery and Underscore, then calling our next file setup.js, which is our setup file for the tests. This is a very simple configuration file and can easily be extended. I thought this would be more useful, but the test framework itself doesn’t need many libraries. I think we need to inject libraries into the webapp’s window for better use (see jquery.simulate example below).

Test Setup
GitHub – setup.js

setup

The test setup sets up the application’s tests. It’s a standard Require JS file that takes in jQuery as a dependency. It saves the jQuery dependency as $$ (line 6). This is to allow the $ variable to point to the iFrame’s jQuery (line 16). We want to perform our test validations against the iFrame’s jQuery, so we’re assigning the iFrame’s jQuery as the standard jQuery variable $.

We import our test suites using the require command. This will look for suite1.js. We can easily add additional require lines for suite2.js, suite3.js, etc.

You’ll notice that we have this code in the iFrame’s load event. This is to refresh all the configurations every time the iFrame loads (on page redirects). We also have QUnit start, which should help us time the tests (not tested thoroughly).

The configurations in the setup are to facilitate test execution. We turn off async (line 19), inject jquery simulate (library to simulate a real click) to the content window (line 23), turn off animations (line 26-27).

Since the iFrame is loaded and the configurations are loaded, we start QUnit.

Test Suite(s)
GitHub – suite1.js

suite1

The test suites are files with the actual QUnit tests. We define a referencePath to avoid cross domain scripting – it makes the test framework agnostic to localhost vs. 127.0.0.1, etc.

This suite has one module that contains one test. The setup and teardown methods are there to show how testing with page redirection works. To explicitly change the URL manually, we use the parent jQuery object ($$) then change the attr of src (line 9). This shouldn’t happen very often as most redirections will be via button clicks in the web application themselves.

You’ll notice that we have two stop() calls on each page redirection. This is because a page redirection triggers the iFrame’s load event, which has a start() call. By having two stop calls, we’re able to ensure the test occurs after the timeout and after the page load. This hasn’t been thoroughly tested yet, but in theory, it seems okay for now.

Other
The GitHub project contains some miscellaneous files like grunt.js and package.json. This is for running the framework via Grunt/Node JS/Phantom JS. We’ve been using this stack for a few months and noticed discrepancies in results between operating systems, phantom JS versions, and browsers. We’re mitigating this issue by having the continuous integration environment run in the same type of environment as the developer machines.

Please let me know if you are using a similar framework as we can share tips and gotchas.

——————————————

** Update

+ When working with async tests, have one QUnit.stop() in the beginning of the test (unless you call asyncTest, which includes this feature) and call one QUnit.start() at the end of the test.  The examples above with multiple stops and starts are not correct.

+ After some testing, we started using the async.series library over the setTimeout.  This practice organizes the tests better and allows for better event usage.  Use events like animationComplete, load, etc.

+ In cases where events only get you half way, sometimes a loop that waits for the condition is a workaround.  Our iFrame’s load event triggered before the page was actually loaded, so we added a loop to check for the jquery object before configuring jquery.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s