Generating HTML fixtures using Zend, Omeka, PHPUnit, and Jasmine

One of the reasons that testing JavaScript can be so pesky (and perhaps one of the reasons that so little JavaScript is tested…) is the fact that you have to maintain a library of HTML “fixtures” for the tests to run on. What’s a fixture? Basically, just a little chunk of markup that provides a sandbox environment for a particular test, or a suite of tests. So, if you have a jQuery widget that adds some extra functionality to a form input, your fixture could be as simple as just a single input tag. And indeed, some of the time you can get away with just manually whipping up a chunk of ad-hoc HTML, dropping it directly into your suite, and testing into the sunset:

<input class="simple-fixture" type="text" />

This works fine, provided that the HTML your code is working on is relatively simple. In practice, though, most front-end applications that grow beyond a certain critical mass of complexity end up with JavaScript code that makes DOM touches on large, complex markup structures that can’t be so easily replicated independent of the application itself.

This issue started to rear its head as Neatline became more and more complex over the course of the last couple months. The Neatline front-end consists of well over 10,000 lines of JavaScript, which works on markup generated by about 20 templates and partials – it would be a frightful headache to have to manually create a library of testing fixtures for the whole application. And even if I took the time to build them all out by hand, I would be committing myself to a labor-intensive, open-ended maintenance task: Every time you make a change to a template, you have to remember to comb through all the files in the fixtures library and replicate the change. Over time – and especially as new developers start working on 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 fantastic post from JB Steadman at Pivotal Labs. Basically, 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. (Details and code after the jump)

Basically, we do this:

  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.

How does this work in practice? Imagine you have a template called _records.php that looks like this:

<div id="container">
  <?php foreach $records as $record: ?>
    <h1><?php echo $record->title; ?></h1>
    <div><?php echo $record->description; ?></div>
  <?php endforeach; ?>
</div>

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

<div id="container">

    <h1>Record 1 Title</h1>
    <div>Description for record 1.</div>

    <h1>Record 2 Title</h1>
    <div>Description for record 2.</div>

</div>

So, the goal here is to create a controller action that populates the template with mock records objects and renders the markup, which can then be captured and saved by an integration “test” that we’ll write in just a minute (test in quotes, since we’re using PHPUnit not so much as a testing framework, but more just as a mechanism for automating requests). First, add a new controller class called FixturesController, and create an action that mocks any variables that need to get pushed into the template:

class YourPlugin_FixturesController extends Omeka_Controller_Action
{

    /**
     * Generate fixture for _records.php.
     *
     * @return void
     */
    public function recordsAction()
    {

        // Turn off the default Zend layout-discovery functionality.
        $this->_helper->viewRenderer->setNoRender(true);

        $record1 = (object) array(
          'title' => 'Record 1 Title',
          'description' => 'A description for record 1.'
        );

        $record2 = (object) array(
          'title' => 'Record 1 Title',
          'description' => 'A description for record 1.'
        );

        $records = array($record1, $record2);

        // Render.
        echo $this->view->partial('public/_records.php', array(
            'records' =>  $records
        ));

    }

}

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 generated markup, and saving the result to the fixtures library.

This should look like this:

class YourPlugin_FixtureBuilderTest extends YourPlugin_Test_AppTestCase
{

    private static $path_to_fixtures = '../spec/javascripts/fixtures/';

    /**
     * Instantiate the helper class, install the plugins, get the database.
     *
     * @return void.
     */
    public function setUp()
    {

        // Set up the testing environment and plugin.
        parent::setUp();
        $this->setUpPlugin();

    }

    /**
     * Fixture builder for _records.php.
     *
     * @return void.
     */
    public function testBuildRecordsMarkup()
    {

        $fixture = fopen(self::$path_to_fixtures . '_records.html', 'w');

        $this->dispatch('your-plugin/fixtures/records');
        $response = $this->getResponse()->getBody('default');

        fwrite($fixture, $response);
        fclose($fixture);

    }

}

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 your fresh-out-of-the-oven fixture should be saved off and ready for action! All that’s left to do now is load the fixture in your JavaScript test, run your code on the HTML, and start enumerating test cases. We use a testing framework called Jasmine in conjunction with a plugin called jasmine-jquery, which provides an easy way to load fixtures into the tests:

/*
 * Unit tests for the records Javascript.
 */

describe('Records', function() {

    var recordsContainer;

    beforeEach(function() {

        // Get the records markup.
        loadFixtures('_records.html');

        // Select the container div.
        browser = $('#container');

        // Instantiate your code.
        browser.recordsWidget();

    });

    // Now, the tests:

    describe('some class of behavior', function() {

        it('should do X', function() {
            expect(true).toEqual(true);
        });

    });

});

Formerly Web Applications Developer on the Scholars' Lab R&D team, David graduated from Yale University with a degree in the Humanities in 2009 and worked as an independent web developer in San Francisco, New York, and Madison, Wisconsin before joining the lab in 2011. David was the lead developer on Neatline and works on research…

1 Comments

  1. Crazy stuff, David, at least the small part that I follow. Thanks for all the help on Neatline, it went over really well at MLA last week- we had tons of useful feedback atthe DHCommons session, I’ll summarize it for you sometime this week.

Pingbacks

Generating Jasmine fixtures with Zend and PHPUnit | david mcClure

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Archives