Generating Jasmine fixtures with Zend and PHPUnit

Cross-posted with edits from scholarslab.org

One of the annoyances of testing client-side Javascript is that it’s often necessary to maintain a library of HTML “fixtures” to run the tests against. A fixture is just a little chunk of markup that provides the “physical” DOM structure to run the code on. So, if you have a jQuery widget that adds extra functionality to a form input, your fixture could be as simple as just a single input tag. When the code is predicated on extremely simple markup, you can get away with just manually generating markup on the fly in the tests:

$('');

In practice, though, non-trivial applications usually require Javascript that makes touches on large, complex, highly-specific markup that can’t realistically be injected into the test suite as inlines jQuery element constructors. Static HTML fixtures can be manually prepared in separate files, but this commits you to a labor-intensive, open-ended maintenance task – every time you make a change to a template, you have to remember to replicate the change in the fixture library. Over time – and especially as new developers come onto the project – there’s a high probability that the “real” HTML generated by the live application will start to diverge from your fixtures.

My search for a solution led me to this excellent post from JB Steadman at Pivotal Labs. He describes a clever method for automatically generating a library of HTML fixtures that uses the server-side test suite as a staging environment that prepares, creates, and saves the markup emitted by the application. That way, your fixtures library can only ever be as old as the last time you ran your back-end test suite, which should be a many-times-daily affair. I was able to implement this pattern in the Omeka/Zend + PHPUnit ecosystem with little difficulty. Basically:

  1. Create a special controller in your application that exists solely for the purpose of rendering the templates (and combinations of templates) that are required by the JavaScript test suite;
  2. Create a series of “testing cases” that issue requests to each of the actions in the fixtures controller, capture the responses, and write the generated HTML directly into the fixtures library.

Imagine you have a template called records.php that looks like this:

And when it’s rendered in the application, the final markup looks like this:

The goal is to create a controller action that populates the template with mock data and renders out the markup, which can then be captured and saved by an integration “test” that we’ll write in just a minute (test in scare quotes, since we’re essentially hijacking PHPUnit and using it as an HTML generator). First, add a new controller class called FixturesController and create an action that mocks any objects that need to get pushed into the template:

Basically, we’re just stubbing out two artificial record objects (for simplicity, we add only the attributes that are used in the template) and directly render the template file as a “partial.” Note the call to setNoRender(true) – by default, Zend will try to automagically discover a template file with the same name as the controller action, but we’re just disabling that functionality since we want direct control over which templates get rendered and in what order.

Next, add a directory called “fixtures” in the /tests directory, and create a file called FixtureBuilderTest.php to house the integration test that will do the work of requesting the new controlled action, capturing the markup, and saving the result to the fixtures library.

This should look like this:

Note that you need to specify the location in the project directory structure that you want to save the fixtures to. In this case, I’m saving to the default location used by Jasmine, but you could point to anywhere in the filesystem relative to the AllTests.php runner file in /tests.

Make sure that the /fixtures directory is included in the test discoverer in AllTests.php, run phpunit, and the fixture should be saved off and ready for use in the front-end suite. Using Jasmine with the jasmine-jquery plugin: