Test in Action – Week 2
  Testing Framework
      Hubert Chan
Testing Framework
• Why using framework?
  – Reinventing the wheel?
  – Frameworks provides
    •   Library / API / Syntactic Sugar
    •   xUnit Pattern
    •   Stub / Mock
    •   Log / Test Coverage
    •   Static Analysis
PHPUnit
• PHPUnit
  – Unit Testing Framework
  – Most common for PHP
  – Open Source
  – Used in known PHP Frameworks
    • Zend Framework
    • Kohana
    • symfony
PHPUnit Installation
• Using pear
  – pear channel discover (only once)
     pear channel-discover pear.phpunit.de
     pear channel-discover components.ez.no
     pear channel-discover pear.symfony-project.com
  – Install PHPUnit
     pear install phpunit/PHPUnit
PHPUnit Example
• Test Code
class StackTest extends PHPUnit_Framework_TestCase {
  public function testPushAndPop() {
    $stack = array();
    $this->assertEquals(0, count($stack));

        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertEquals(1, count($stack));

        $this->assertEquals('foo', array_pop($stack));
        $this->assertEquals(0, count($stack));
    }
}
PHPUnit Test Cases Rule
• General Rule
  1. The tests for a class Class go into a class ClassTest.
  2. ClassTest inherits (most of the time) from
     PHPUnit_Framework_TestCase.
  3. The tests are public methods that are named test*.
  4. Write Assertions
Data Provider
• Data Provider
  – Return an iterative object (Iterator interface)
  – Use for data-driven testing
     • Like ACM Input data
  – Use it carefully
Data Provider Sample
• Test Code
class DataTest extends PHPUnit_Framework_TestCase {
  /**
  * @dataProvider provider
  */
  public function testAdd($a, $b, $c) {
     $this->assertEquals($c, $a + $b);
  }
    public function   provider() {
      return array(
        array(0, 0,   0),
        array(0, 1,   1),
        array(1, 0,   1),
        array(1, 1,   3)
      );
    }
}
Testing Exception
• Testing Exception
  – Testing for failure cases
  – Error Handling
  – Examine
     • Exception Type
     • Exception Message
     • Exception Code
Testing Exception Sample
• Test Code
class ExceptionTest extends PHPUnit_Framework_TestCase {
  /**
  * @expectedException        InvalidArgumentException
  * @expectedExceptionMessage Right Message
  */
  public function testExceptionHasRightMessage() {
    throw new InvalidArgumentException('Some Message', 10);
  }
    /**
    * @expectedException     InvalidArgumentException
    * @expectedExceptionCode 20
    */
    public function testExceptionHasRightCode() {
      throw new InvalidArgumentException('Some Message', 10);
    }
}
Testing Exception Sample
• Test Code
class ExceptionTest extends PHPUnit_Framework_TestCase {

    public function testExceptionHasRightMessage() {
      $this->setExpectedException(
        'InvalidArgumentException', 'Right Message'
      );
      throw new InvalidArgumentException('Some Message', 10);
    }
    public function testExceptionHasRightCode() {
      $this->setExpectedException(
        'InvalidArgumentException', 'Right Message', 20
      );
      throw new InvalidArgumentException(
        'The Right Message', 10
      );
    }
}
Assertion
• Assertions
  – assertEquals
  – assertTrue / assertFalse
  – assertContains
  – and etc.
Assertion Guideline
• Assertion Guideline
  – Using best suitable assertion
  – Some assertions are more easy to use
     • assertXmlFileEqualsXmlFile()
     • assertRegExp()
     • and etc.
  – Make assertion contains useful message
Assertion Compare - Equals
• Test Code
class EqualTest extends PHPUnit_Framework_TestCase {

    public function test_AssertTrue() {
        $this->assertTrue("123" === "456");
    }

    public function test_AssertEquals() {
        $this->assertEquals("123", "456");
    }
}
Assertion Compare – Equals (cont.)
• Output
1) EqualTest::test_AssertTrue
Failed asserting that <boolean:false> is true.

/usr/home/hubert/tmp/php/equal_compare.php:6

2) EqualTest::test_AssertEquals
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-123
+456

/usr/home/hubert/tmp/php/equal_compare.php:10
Assertion Message
• Prototype
 assertEmpty(mixed $actual[, string $message = ''])

• Test Code
 class EqualTest extends PHPUnit_Framework_TestCase {
   public function test_AssertMessage() {
     $handler = new LogViewHandler();
     $this->assertNotEmpty(
       $handler->generate_ajax(),
       "generate_ajax() should not be empty!!"
     );
   }
 }
Assertion Message - Output
• Output
1) EqualTest::test_AssertMessage
generate_ajax() should not be empty!!
Failed asserting that a string is not empty.

/usr/home/hubert/tmp/php/equal_compare.php:24
xUnit Architecture
• Test Case
• Test fixtures
  – Pre-action / Post-action needed to run a test
• Test Suites
  – A set of tests that all share the same fixture
  – The order of the tests shouldn't matter
Test Fixtures
• Fixtures
  – Avoid duplicate Arrangement code
  – Make test cases focus on Action and Assertion
• Test Fixtures
  – setUp()
     • Pre-action before each test case
  – tearDown()
     • Post-action after each test case
Test Execution
Fixture Example
• Test Code
class StackTest extends PHPUnit_Framework_TestCase {
  protected $stack;
    protected function setUp() {
      $this->stack = array();
    }
    public function testEmpty() {
      $this->assertTrue(empty($this->stack));
    }
    public function testPush() {
      array_push($this->stack, 'foo');
      $this->assertEquals('foo', $this->stack[count($this->stack)-1]);
      $this->assertFalse(empty($this->stack));
    }
    public function testPop() {
      array_push($this->stack, 'foo');
      $this->assertEquals('foo', array_pop($this->stack));
      $this->assertTrue(empty($this->stack));
    }
}
Fixture Guideline
• Fixture Guideline
  – Only use setUp and tearDown to initialize or
    destroy objects that are shared throughout the
    test class in all the tests
     • Otherwise, readers don’t know which tests use the
       logic inside the setup method and which don’t
Sharing Fixture
• Sharing Fixture
  – Really few reason to share fixtures
  – Good example
       • Reuse database connection
  – Sample Test Code
  class DatabaseTest extends PHPUnit_Framework_TestCase {
    protected static $dbh;

      public static function setUpBeforeClass() {
        self::$dbh = new PDO('sqlite::memory:');
      }

      public static function tearDownAfterClass() {
        self::$dbh = NULL;
      }
  }
Organize PHPUnit Tests
• Mapping for production code and test
 middleware_rev                  tests
 |── handlers                    |── _log
 │ └── _details                  |── bootstrap.php
 │   |── TRConverter.class.php   |── handlers
 │   └── tmql                    │ └── _details
 │      └──                      │     |── TRConverterTest.php
 TMQLEscape.class.php            │     └── tmql
 └── lib                         │       └──
                                 TMQLEscapeTest.php
                                 |── lib
                                 └── phpunit.xml
Execute PHPUnit tests
• Execute all tests
  % phpunit
• Execute all tests in a subdirectory
  % phpunit handlers
• Execute single test in a subdirectory
  % phpunit --filter TMQLEscapeTest handlers
Test Naming
• Test Class Name
  – For class name “Util”
  – Test class name should be “UtilTest”
  – Test filename should be “UtilTest.php”
Test Naming
• Function Name
  – Test function name should be
    test_<function>_<scenario>_<expect_behavior>
  – Example
    • test_escape_evenBackSlashesData_successEscape
Single Assertion / Concept per Test
• Single Assertion / Concept per Test
  – If a test failed, the cause may be more obvious
  – Compare
     • PHPUnit Example
     • Fixture Example
  – testPushPop test 3 concepts
     • Test for Empty / Push / Pop
Clean Test
• Clean Test
  – Keeping Tests Clean
  – Or you will not maintain them
  – Test codes are as important code as production
  – Write Test API / Utility / DSL
  – Clean TDD cheat sheet
     • http://www.planetgeek.ch/2011/01/04/clean-code-
       and-clean-tdd-cheat-sheets/
F.I.R.S.T
• F.I.R.S.T
   – Fast
   – Independent (isolated)
   – Repeatable
   – Self-Validating
   – Timely
References
• Clean Code
• The Art of Unit Testing

Test in action week 2

  • 1.
    Test in Action– Week 2 Testing Framework Hubert Chan
  • 2.
    Testing Framework • Whyusing framework? – Reinventing the wheel? – Frameworks provides • Library / API / Syntactic Sugar • xUnit Pattern • Stub / Mock • Log / Test Coverage • Static Analysis
  • 3.
    PHPUnit • PHPUnit – Unit Testing Framework – Most common for PHP – Open Source – Used in known PHP Frameworks • Zend Framework • Kohana • symfony
  • 4.
    PHPUnit Installation • Usingpear – pear channel discover (only once) pear channel-discover pear.phpunit.de pear channel-discover components.ez.no pear channel-discover pear.symfony-project.com – Install PHPUnit pear install phpunit/PHPUnit
  • 5.
    PHPUnit Example • TestCode class StackTest extends PHPUnit_Framework_TestCase { public function testPushAndPop() { $stack = array(); $this->assertEquals(0, count($stack)); array_push($stack, 'foo'); $this->assertEquals('foo', $stack[count($stack)-1]); $this->assertEquals(1, count($stack)); $this->assertEquals('foo', array_pop($stack)); $this->assertEquals(0, count($stack)); } }
  • 6.
    PHPUnit Test CasesRule • General Rule 1. The tests for a class Class go into a class ClassTest. 2. ClassTest inherits (most of the time) from PHPUnit_Framework_TestCase. 3. The tests are public methods that are named test*. 4. Write Assertions
  • 7.
    Data Provider • DataProvider – Return an iterative object (Iterator interface) – Use for data-driven testing • Like ACM Input data – Use it carefully
  • 8.
    Data Provider Sample •Test Code class DataTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provider */ public function testAdd($a, $b, $c) { $this->assertEquals($c, $a + $b); } public function provider() { return array( array(0, 0, 0), array(0, 1, 1), array(1, 0, 1), array(1, 1, 3) ); } }
  • 9.
    Testing Exception • TestingException – Testing for failure cases – Error Handling – Examine • Exception Type • Exception Message • Exception Code
  • 10.
    Testing Exception Sample •Test Code class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException * @expectedExceptionMessage Right Message */ public function testExceptionHasRightMessage() { throw new InvalidArgumentException('Some Message', 10); } /** * @expectedException InvalidArgumentException * @expectedExceptionCode 20 */ public function testExceptionHasRightCode() { throw new InvalidArgumentException('Some Message', 10); } }
  • 11.
    Testing Exception Sample •Test Code class ExceptionTest extends PHPUnit_Framework_TestCase { public function testExceptionHasRightMessage() { $this->setExpectedException( 'InvalidArgumentException', 'Right Message' ); throw new InvalidArgumentException('Some Message', 10); } public function testExceptionHasRightCode() { $this->setExpectedException( 'InvalidArgumentException', 'Right Message', 20 ); throw new InvalidArgumentException( 'The Right Message', 10 ); } }
  • 12.
    Assertion • Assertions – assertEquals – assertTrue / assertFalse – assertContains – and etc.
  • 13.
    Assertion Guideline • AssertionGuideline – Using best suitable assertion – Some assertions are more easy to use • assertXmlFileEqualsXmlFile() • assertRegExp() • and etc. – Make assertion contains useful message
  • 14.
    Assertion Compare -Equals • Test Code class EqualTest extends PHPUnit_Framework_TestCase { public function test_AssertTrue() { $this->assertTrue("123" === "456"); } public function test_AssertEquals() { $this->assertEquals("123", "456"); } }
  • 15.
    Assertion Compare –Equals (cont.) • Output 1) EqualTest::test_AssertTrue Failed asserting that <boolean:false> is true. /usr/home/hubert/tmp/php/equal_compare.php:6 2) EqualTest::test_AssertEquals Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -123 +456 /usr/home/hubert/tmp/php/equal_compare.php:10
  • 16.
    Assertion Message • Prototype assertEmpty(mixed $actual[, string $message = '']) • Test Code class EqualTest extends PHPUnit_Framework_TestCase { public function test_AssertMessage() { $handler = new LogViewHandler(); $this->assertNotEmpty( $handler->generate_ajax(), "generate_ajax() should not be empty!!" ); } }
  • 17.
    Assertion Message -Output • Output 1) EqualTest::test_AssertMessage generate_ajax() should not be empty!! Failed asserting that a string is not empty. /usr/home/hubert/tmp/php/equal_compare.php:24
  • 18.
    xUnit Architecture • TestCase • Test fixtures – Pre-action / Post-action needed to run a test • Test Suites – A set of tests that all share the same fixture – The order of the tests shouldn't matter
  • 19.
    Test Fixtures • Fixtures – Avoid duplicate Arrangement code – Make test cases focus on Action and Assertion • Test Fixtures – setUp() • Pre-action before each test case – tearDown() • Post-action after each test case
  • 20.
  • 21.
    Fixture Example • TestCode class StackTest extends PHPUnit_Framework_TestCase { protected $stack; protected function setUp() { $this->stack = array(); } public function testEmpty() { $this->assertTrue(empty($this->stack)); } public function testPush() { array_push($this->stack, 'foo'); $this->assertEquals('foo', $this->stack[count($this->stack)-1]); $this->assertFalse(empty($this->stack)); } public function testPop() { array_push($this->stack, 'foo'); $this->assertEquals('foo', array_pop($this->stack)); $this->assertTrue(empty($this->stack)); } }
  • 22.
    Fixture Guideline • FixtureGuideline – Only use setUp and tearDown to initialize or destroy objects that are shared throughout the test class in all the tests • Otherwise, readers don’t know which tests use the logic inside the setup method and which don’t
  • 23.
    Sharing Fixture • SharingFixture – Really few reason to share fixtures – Good example • Reuse database connection – Sample Test Code class DatabaseTest extends PHPUnit_Framework_TestCase { protected static $dbh; public static function setUpBeforeClass() { self::$dbh = new PDO('sqlite::memory:'); } public static function tearDownAfterClass() { self::$dbh = NULL; } }
  • 24.
    Organize PHPUnit Tests •Mapping for production code and test middleware_rev tests |── handlers |── _log │ └── _details |── bootstrap.php │ |── TRConverter.class.php |── handlers │ └── tmql │ └── _details │ └── │ |── TRConverterTest.php TMQLEscape.class.php │ └── tmql └── lib │ └── TMQLEscapeTest.php |── lib └── phpunit.xml
  • 25.
    Execute PHPUnit tests •Execute all tests % phpunit • Execute all tests in a subdirectory % phpunit handlers • Execute single test in a subdirectory % phpunit --filter TMQLEscapeTest handlers
  • 26.
    Test Naming • TestClass Name – For class name “Util” – Test class name should be “UtilTest” – Test filename should be “UtilTest.php”
  • 27.
    Test Naming • FunctionName – Test function name should be test_<function>_<scenario>_<expect_behavior> – Example • test_escape_evenBackSlashesData_successEscape
  • 28.
    Single Assertion /Concept per Test • Single Assertion / Concept per Test – If a test failed, the cause may be more obvious – Compare • PHPUnit Example • Fixture Example – testPushPop test 3 concepts • Test for Empty / Push / Pop
  • 29.
    Clean Test • CleanTest – Keeping Tests Clean – Or you will not maintain them – Test codes are as important code as production – Write Test API / Utility / DSL – Clean TDD cheat sheet • http://www.planetgeek.ch/2011/01/04/clean-code- and-clean-tdd-cheat-sheets/
  • 30.
    F.I.R.S.T • F.I.R.S.T – Fast – Independent (isolated) – Repeatable – Self-Validating – Timely
  • 31.
    References • Clean Code •The Art of Unit Testing