PHPUnit

Install PHPUnit

Install pear (if not done)

Install pear on Windows

  • Download http://pear.php.net/go-pear.phar from a browser
  • Install pear and enter 1 to change your installation directory
    php go-pear.phar
  • Add the pear installation directory to the system path

Install pear on Mac

  • Install pear
    wget http://pear.php.net/go-pear.phar
    sudo php -d detect_unicode=0 go-pear.phar
    Enter 1 and use /usr/lib/php as the installation base
  • Add the pear installation directory to the include_path of /etc/php.ini
    • If /etc/php.ini does not exist
      sudo cp /etc/php.ini.default /etc/php.ini
    • Add pear directory to the include_path
      /etc/php.ini
      include_path=".:/usr/lib/php/pear/"

Verification

pear version

Install PHPUnit

Install PHPUnit on Windows

pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit

Install PHPUnit on Mac

sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover components.ez.no
sudo pear channel-discover pear.symfony-project.com
sudo pear install phpunit/PHPUnit

PHPUnit Test

Simple PHPUnit test

<?php
class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomehting()
    {
        $s = array();
        $this->assertEquals(0, count($s));

        array_push($s, 'e1');
        $this->assertEquals('e1', $s[count($s)-1]);
        $this->assertEquals(1, count($s));
        return $s
     }
}

PHPUnit test dependency using @depends

  • With PHPUnit @depends, testAfterSomething utilizes the return value of testSomething to run the test

Simple PHPUnit test

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomehting()
    {
        ...
        return $s
     }

     /**
     * @depends testSomething
     */
    public function testAfterSomething(array $s)
    {
       ...
    }
}

PHPUnit Testing Setup, teardown (PHPUnit Test Fixture)

class MyClassTest extends PHPUnit_Framework_TestCase
{
    protected $s;

    protected function setUp()
    {
        $this->s = array();
    }

    protected function tearDown()
    {
        ...
    }

    public function testSomething()
    {
       $this-s;
       ...
    }
  • setUp and tearDown run once for every testing method
  • setUpBeforeClass() and tearDownAfterClass() run one before/after the tests

PHPUnit Test Data Provider with @dataProvider

Provide a PHPUnit test data provider to supply data to a testing method

class MyClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider provider
     */
    public function testSomething($a, $b, $c)
    {
        ...
    }

    public function provider()
    {
        return array(
          array(0, 0, 0),
          array(0, 1, 1),
          array(1, 0, 1),
          array(1, 1, 3)
        );
    }
}

PHPUnit Testing Exception

PHPUnit @expectedException tests whether an exception is thrown inside a testing method.

class MyClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException SomeException
     */
    public function testException()
    {
        throw new SomeException();
    }
}

Also verify if the exception contain specific message and value

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException        SomeException
     * @expectedExceptionMessage A Message
     */
    public function testException()
    {
        throw new SomeException('Some Message', 20);
    }

    /**
     * @expectedException     SomeException
     * @expectedExceptionCode 10
     */
    public function testExceptionHasRightCode()
    {
        throw new SomeException('Some Message', 20);
    }
}

Detect PHP errors, warnings or notices

{
    /**
     * @expectedException PHPUnit_Framework_Error
     */
    public function testException()
    {
        // Force a PHP error
        include 'none_exist.php';
    }
}
  • PHPUnit_Framework_Error for error
  • PHPUnit_Framework_Warning for warning
  • PHPUnit_Framework_Notice for notice

Mark the method as incomplete or skipped

Mark the testing method incomplete

public function testSomething() {
        $this->markTestIncomplete(
          'Incomplete'
        );
    }

Skip the test method

public function testSomething() {
            $this->markTestSkipped(
              'Skip'
            );
    }

PHPUnit output testing

PHPUnit Test specific output

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testOutput()
    {
        $this->expectOutputString('v1');
        print 'v1';
    }

}

Stubbed Objects

final, private and static methods cannot be stubbed or mocked

Use HTTPUnit stub to return a fixed value

require_once 'AClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        // Create a stub for the AClass
        $stub = $this->getMock('AClass');

        // Configure the stub
        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->returnValue('v1'));

        $this->assertEquals('v1', $stub->doSomething());
    }
}
?>

Use HTTPUnit stub to return a value from a parameter of the method call

require_once 'AClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $stub = $this->getMock('AClass');

        // Configure a stub that will return the input parameter
        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->returnArgument(0));

        // Mock object return the method parameter as output
        $this->assertEquals('p1', $stub->doSomething('p1'));

    }
}

The HTTPUnit stub object returns itself

require_once 'AClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $stub = $this->getMock('AClass');

        // Configure the stub.
        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->returnSelf());

        $this->assertSame($stub, $stub->doSomething());
    }
}

Use a map to determined the output value from the input parameters for a PHPUnit stub object

require_once 'AClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $stub = $this->getMock('AClass');

        // Create a map of arguments to return values.
        $map = array(
          array('a', 'b', 'c'),
          array('d', 'e', 'f')
        );

        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->returnValueMap($map));

        $this->assertEquals('c', $stub->doSomething('a', 'b'));
        $this->assertEquals('f', $stub->doSomething('d', 'e'));
    }
}

Use a callback for the returned value of a PHPUnit stub class

require_once 'AClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $stub = $this->getMock('AClass');

        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->returnCallback('strtoupper'));

        $this->assertEquals('ABC', $stub->doSomething('abc'));
    }
}

Return a sequence of values from the PHPUnit stub object

require_once 'AClass.php';

class MyTestTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $stub = $this->getMock('AClass');

        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->onConsecutiveCalls(1, 2, 3));

        $this->assertEquals(1, $stub->doSomething());
        $this->assertEquals(2, $stub->doSomething());
        $this->assertEquals(3, $stub->doSomething());
    }
}

Use a PHPUnit stub object to throw an exception

require_once 'AClass.php';

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $stub = $this->getMock('SomeClass');

        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->throwException(new Exception));

        // $stub->doSomething() throws Exception
        $stub->doSomething();
    }
}

GetMock

  • Methods of the mocked class are replaced with methods that returns NULL unless it is overridden by expects
  • The second optional parameter are an array of method names that will be mocked
  • The third optional parameter hold a parameter array passed to the original class constructor
  • The fourth optional parameter specifies a class name for the generated mock class
  • The fifth optional parameter disable the call to the original class constructor
  • The sixth optional parameter disable the call to the original class clone constructor.
  • The seventh optional parameter disable __autoload() during the generation of the mock class

PHPUnit Mock object

A PHPUnit mock object behaves like a test stub plus assertions

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        // Create a mock for an Observer class and mock the update() method only
        $observer = $this->getMock('Observer', array('update'));

        // Set up the expectation for the update() method to be called only once with parameter 'p1'
        $observer->expects($this->once())
                 ->method('update')
                 ->with($this->equalTo('p1'));

        // Create a Subject object and attach the mock object
        $subject = new Subject;
        $subject->attach($observer);

        // Call the doSomething() method on $subject
        // which we expect to call the mock object's update with "p1" parameter
        $subject->doSomething();
    }
}

Using more constraint with the "with" methods

class MyClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $observer = $this->getMock('Observer', array('something'));

        $observer->expects($this->once())
                 ->method('logSomething')
                 ->with($this->greaterThan(0),
                        $this->stringContains('p1'),
                        $this->anything());

        $subject = new Subject;
        $subject->attach($observer);

         $subject->something();
    }
}

phpunit command line

phpunit --help
PHPUnit 3.5.15 by Sebastian Bergmann.

Usage: phpunit [switches] UnitTest [UnitTest.php]
       phpunit [switches] <directory>

  --log-junit <file>        Log test execution in JUnit XML format to file.
  --log-tap <file>          Log test execution in TAP format to file.
  --log-dbus                Log test execution to DBUS.
  --log-json <file>         Log test execution in JSON format.

  --coverage-html <dir>     Generate code coverage report in HTML format.
  --coverage-clover <file>  Write code coverage data in Clover XML format.

  --testdox-html <file>     Write agile documentation in HTML format to file.
  --testdox-text <file>     Write agile documentation in Text format to file.

  --filter <pattern>        Filter which tests to run.
  --group ...               Only runs tests from the specified group(s).
  --exclude-group ...       Exclude tests from the specified group(s).
  --list-groups             List available test groups.

  --loader <loader>         TestSuiteLoader implementation to use.
  --repeat <times>          Runs the test(s) repeatedly.

  --tap                     Report test execution progress in TAP format.
  --testdox                 Report test execution progress in TestDox format.

  --colors                  Use colors in output.
  --stderr                  Write to STDERR instead of STDOUT.
  --stop-on-error           Stop execution upon first error.
  --stop-on-failure         Stop execution upon first error or failure.
  --stop-on-skipped         Stop execution upon first skipped test.
  --stop-on-incomplete      Stop execution upon first incomplete test.
  --strict                  Mark a test as incomplete if no assertions are made.

  --verbose                 Output more verbose information.
  --wait                    Waits for a keystroke after each test.

  --skeleton-class          Generate Unit class for UnitTest in UnitTest.php.
  --skeleton-test           Generate UnitTest class for Unit in Unit.php.

  --process-isolation       Run each test in a separate PHP process.
  --no-globals-backup       Do not backup and restore $GLOBALS for each test.
  --static-backup           Backup and restore static attributes for each test.
  --syntax-check            Try to check source files for syntax errors.

  --bootstrap <file>        A "bootstrap" PHP file that is run before the tests.

  -c|--configuration <file> Read configuration from XML file.
  --no-configuration        Ignore default configuration file (phpunit.xml).
  --include-path <path(s)>  Prepend PHP's include_path with given path(s).
  -d key[=value]            Sets a php.ini value.

  --help                    Prints this usage information.
  --version                 Prints the version and exits.

  --debug                   Output debugging information.

Sample command: test a PHPUnit test file

phpunit mytest.php

With wide card

phpunit testDir/*mytest.php

Sample command: test all PHPUnit test under a directory

phpunit testDir

PHPUnit Test Code Coverage

To generate a PHPUnit test code coverage report

phpunit --coverage-html ./result MyClassTest

Specify the method(s) that the testing method is testing

  • only the code coverage information for the specified method(s) will be produced
    class MyClassTest extends PHPUnit_Framework_TestCase
    {
        /**
         * @covers MyClass::something
         */
        public function testSomething()
        {
            ...
        }

To ignore methods on the PHPUnit coverage report

/**
     * @codeCoverageIgnore
     */
    public function something()
    {
    }

To ignore codes on the PHPUnit coverage report

// @codeCoverageIgnoreStart
    ...
    // @codeCoverageIgnoreEnd

PHPUnit Testing with Selenium on Browser

<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class MyWebTest extends PHPUnit_Extensions_SeleniumTestCase
{
    protected function setUp()
    {
        $this->setBrowser('*firefox');
        $this->setBrowserUrl('http://www.example.com/');
    }

    public function testTitle()
    {
        $this->open('http://www.mysite.com/');
        $this->assertTitle('My Site');
    }
}
?>

Capture Screen shoot when the test failed

<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
    protected $captureScreenshotOnFailure = TRUE;
    protected $screenshotPath = '~/docroot';
    protected $screenshotUrl = 'http://localhost/screenshots';

    protected function setUp()
    {
        $this->setBrowser('*firefox');
        $this->setBrowserUrl('http://www.mysite.com/');
    }

}
?>