Intro to PHPUnit


PHP Saigon Meetup
07 Aug 2014

Thế Hồng (Andy) Trương (张)
v3k.net

/me

  • PHP/Drupal developer since 2006
  • Admin of Drupal Vietnam [org/FB Group]
  • Contributor of GO1





Why QA?




Good code is testable code

Don't trust 

our own code

Your code is sh*t!
Don't believe?
Ask your yesterday-self

Don't trust 

other people's code


  • Make sure external library usages are correct
  • Easy updating a library X from 1.0 to 1.1

Help 

large code base MANAGEABLE

How be a patch RTBC 

without test-bot's status?


Why?


PHPUnit

Why? It's Popular


Overall: 2 446 748 installs
30 days: 290 755 installs
Today: 11 547 installs
via packagist.org @today

Supported by Symfony, Drupal,  Travis, …

Why? It's Easy install

$ # Usage phar$ wget https://phar.phpunit.de/phpunit.phar$ chmod +x phpunit.phar$ mv phpunit.phar /usr/local/bin/phpunit
$ # Using composer to require phpunit for your own project$ echo '{ "require-dev": { "phpunit/phpunit": "~4.1" } }' > composer.json
$ # Using composer to install global$ composer global require "phpunit/phpunit=4.1.*"

Why? IT's Easy using

Define it
class My_Class_Test extends PHPUnit_Framework_TestCase {
    public function testMethodX() { $this->assertTrue(true); }
    public function testMethodY() { $this->assertFalse(true, ''); }
}
Run it
$ phpunit My_Class_Test.php
PHPUnit 4.1.3 by Sebastian Bergmann.

.F

Time: 43 ms, Memory: 3.00Mb

There was 1 failure:
1) My_Class_Test::testMethodY
Failed assert
Failed asserting that true is false.

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

Why? feature rich

  1. 40 assert* methods
  2. @dataSource
  3. @require function yaml_emit
  4. @require extension apc
  5. @require PHP 5.4
  6. @depends
  7. Test exception
  8. Test double
  9. Test database
  10. Test coverage (require xdebug)
  11. Selenium testing



Why not?



How can i…

organise the test structure

# From http://phpunit.de/manual/4.1/en/organizing-tests.html

src                                 tests
`-- autoload.php                    `-- bootstrap.php
`-- Currency.php                    `-- CurrencyTest.php
`-- IntlFormatter.php               `-- IntlFormatterTest.php
`-- Money.php                       `-- MoneyTest.php


define a test case

class My_Class_Test extends PHPUnit_Framework_TestCase {
    public function setUpBeforeClass() {}
    public function setUp() {}

    public function testMethodX() { $this->assertTrue(true); }
    public function testMethodY() { $this->assertFalse(true, ''); }

    public function tearDown() {}
    public function tearDownAfterClass() {}
}

Write config file


Test exception

class My_Class_Test extends PHPUnit_Framework_TestCase {

  /**
   * @expectedException RuntimeException
   * @expectedExceptionMessage Unimplemented functionality
   */
  public function testException() {
    // try things…
  }

}

Test protected/private methods?

class My_Class_Test extends PHPUnit_Framework_TestCase {
public function testInvisibleMethod() { $obj = new My_Class(); $ref = new ReflectionClass($obj); $ref->getMethod('myPrivateMethod')->setAccessible(true); $results = $obj-> myPrivateMethod(); $this->assertNotEmpty($results); } }

Test double

class My_Class_Test extends PHPUnit_Framework_TestCase
{

    public function testDouble()
    {
        $generator = $this->getMock('PDFGenerator');
        $generator
            ->expects($this->once())
            ->method('generate')
            ->with('<html>…')
            ->willReturn(new PDFFile());

        $my_web_page = new WebPage();
        $my_web_page->print($driver = 'pdf');
    }

}

Test coverage

depends on xDebug

is really useful to

explore units of code

which are dead, untrusted

$ phpunit --coverage-text
$ phpunit --coverage-html /path/to/report
$ # screenshot => google images

More

  • http://phpunit.de/manual/current/en/index.html
  • CodeCeption
  • Behat
  • Mink

Examples

  • https://github.com/sebastianbergmann/money
  • https://github.com/fabpot/Twig



Question please


Andy Truong @GO1
Made with Slides.com