Unit test a component that generates a chunk of HTML

On my way home from work today I wrote some unit tests for some components of an internal framework we have at work. Some of the components produce small chunks of HTML, be it the header of a page, the main menu, or an ad.

There are probably loads of ways to write unit tests that enables you to test the output from such a component. I thought I could share with you how I decided to do it, and hopefully someone out there might benefit from it..

One of the components I wrote a tests for produces several <ul> lists with some nested <ol> lists. Lets imagine the output would look something like this:

<div id="menu">
  <ul id="news">
    <li>
      main 1
      <ol id="business" class="active">
        <li>sub 1.1</li>
        <li>sub 1.2</li>
      </ol>
    </li>
    <li>
      main 2
      <ol id="politics">
        <li>sub 2.1</li>
        <li>sub 2.2</li>
      </ol>
    </li>
  </ul>
  <ul id="sports">
    <li>
      main 3
      <ol id="football">
        <li>sub 3.1</li>
        <li>sub 3.2</li>
      </ol>
    </li>
    <li>
      main 4
      <ol id="golf">
        <li>sub 4.1</li>
        <li>sub 4.2</li>
      </ol>
    </li>
  </ul>
</div>

By default all the nested <ol> lists are retracted except the first one. The list that we want to have expanded is given a css class called active. Here is how we use the component:

<?php
// Print the default menu (with the first <ol> expanded)
$menu = new VG_Page_Element_Menu();
print($menu);

// Print the menu with the "golf" <ol> expanded
$options = array('activeSection' => VG_Page_Element_Menu::SECTION_GOLF);
$menu = new VG_Page_Element_Menu($options);
print($menu);

Now, how to test this? I used SimpleXML and xpath for this case. My test class looks something like this:

<?php
class VG_Page_Element_Menu_MenuTest extends PHPUnit_Framework_TestCase {
    /**
     * Local menu element
     *
     * @var VG_Page_Element_Menu
     */
    protected $element = null;

    /**
     * Set up method
     * 
     * Create a fresh menu element before every test
     */
    public function setUp() {
        $this->element = new VG_Page_Element_Menu();
    }

    /**
     * Tear down method
     * 
     * Destroy the menu element after every test
     */
    public function tearDonw() {
        $this->element = null;
    }

    /**
     * Test the default output
     */   
     public function testRenderDefaultMenu() {
        $menu = simplexml_load_string((string) $this->element);
        $activeSection = $menu->xpath('/div/ul[@id="news"]/li/ol[@id="business" and @class="active"]');

        // Assert that we have one hit
        $this->assertSame(1, count($activeSection));

        // Assert that there is only one expanded section in total
        $activeSections = $menu->xpath('/div/ul/li/ol[@class="active"]');
        $this->assertSame(1, count($activeSections));
    }

    /**
     * Test output with other section expanded
     */
    public function testRenderNonDefaultMenuWithExpandedSection() {
        $section = VG_Page_Element_Menu::SECTION_GOLF;
        $this->element->setOption('activeSection', $section);
        $menu = simplexml_load_string((string) $this->element);
        $activeSection = $menu->xpath('/div/ul[@id="sports"]/li/ol[@id="' . $section . '" and @class="active"]');

        // Assert that we have one hit
        $this->assertSame(1, count($activeSection));

        // Assert that there is only one expanded section in total
        $activeSections = $menu->xpath('/div/ul/li/ol[@class="active"]');
        $this->assertSame(1, count($activeSections));
    }
}

In the first test I look for an <ol> element with id == "business" and class == "active", inside an <li> element, inside an <ul> element with id == "news", inside a <div>. I do a similar check in the other test but with some other values for the id’s of the two lists.

As I mentioned there are probably loads of ways to solve this, but I happened to like this approach. Comments anyone?

Advertisements
This entry was posted in PHP, Technology, Work related and tagged , , , , , . Bookmark the permalink.

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