Monday, August 22, 2011

web/user interface test with phpunit only

The best tool to test user interface is selenium(http://hengrui-li.blogspot.com/2011/08/selenium-user-interface-test-with.html). However, in case we can't use selenium, we can still test user interface with phpunit by using DOMDocument and DOMXPath. Let's check this code:

class SampleTest extends PHPUnit_Framework_TestCase
{
    /**
     * Prepares the environment before running a test.
     */
    protected function setUp()
    {
        parent::setUp();
        $_SERVER['REQUEST_METHOD'] = 'GET';
        $this->setDb();
    }

    /**
     * set up database
     * @throws Exception
     */
    protected function setDb()
    {
        shell_exec('mysql -uroot -proot test < ' . dirname(__FILE__) . '/../fixtures/test.sql');
    }

    public function testIndex()
    {
        ob_start();
        Sample::index();
        $output = ob_get_contents();
        ob_end_clean();
        ob_end_flush();
        try {
            $dom = new DOMDocument();
            $dom->loadHTML($output);
            $xpath = new DOMXPath($dom);
            $this->assertEquals('Test Title', $this->getText($xpath, '//head/title'));
            $this->assertEquals('Test Header', $this->getText($xpath, '//div[@id="header"]/h1'));
            $this->assertEquals('Last Name', $this->getText($xpath, '//table/tr/th[1]'));
            $this->assertEquals('First Name', $this->getText($xpath, '//table/tr/th[2]'));
            $this->assertEquals("Henry", $this->getValue($xpath, '//input[@name="firstname"]'));
        } catch ( Exception $e ) {
            $this->fail('Not valid dom document - ' . $e->getMessage());
        }
    }

    private function getText(DOMXPath $xpath, $query)
    {
        if ($xpath->query($query)->length == 0) {
            throw new Exception('Text not found in query ' . $query);
        }
        return $xpath->query($query)->item(0)->nodeValue;
    }

    private function getValue(DOMXPath $xpath, $query)
    {
        return $xpath->query($query)->item(0)->getAttribute('value');
    }
}
This code will output the index page:


        ob_start();
        Sample::index();
        $output = ob_get_contents();
        ob_end_clean();
        ob_end_flush();

Then we create a DOM object and load the html output. For a legacy web page, it is quite possible that the html output is not a valid DOM document. So this test can help us fixing our html pages.

$dom = new DOMDocument();
$dom->loadHTML($output);

Then we create a DOMXPath object for the $dom object so we can use DOMXPath's powerful methods to query our DOM documents.

$xpath = new DOMXPath($dom);
            
Code below is actually checking the content of the DOM document.

$this->assertEquals('Test Title', $this->getText($xpath, '//head/title'));
$this->assertEquals('Test Header', $this->getText($xpath, '//div[@id="header"]/h1'));
$this->assertEquals('Last Name', $this->getText($xpath, '//table/tr/th[1]'));
$this->assertEquals('First Name', $this->getText($xpath, '//table/tr/th[2]'));
$this->assertEquals("Henry", $this->getValue($xpath, '//input[@name="firstname"]'));

As we can see, testing web interface in this way is not very enjoyable and it can't properly test javascript's DOM manipulation functions. But, it is better than none.



No comments: