Test in action week 2

Preview:

Citation preview

Test in Action – Week 2Testing 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.depear channel-discover components.ez.nopear channel-discover pear.symfony-project.com

– Install PHPUnitpear install phpunit/PHPUnit

PHPUnit Example

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)); }}

• Test Code

PHPUnit Test Cases Rule

• General Rule1. 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

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) ); }}

• Test Code

Testing Exception

• Testing Exception– Testing for failure cases– Error Handling– Examine• Exception Type• Exception Message• Exception Code

Testing Exception Sample

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); }}

• Test Code

Testing Exception Sample

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 ); }}

• Test Code

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 Codeclass 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• Output1) EqualTest::test_AssertTrueFailed asserting that <boolean:false> is true.

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

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

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

Assertion Message

• Test Codeclass EqualTest extends PHPUnit_Framework_TestCase { public function test_AssertMessage() { $handler = new LogViewHandler(); $this->assertNotEmpty( $handler->generate_ajax(), "generate_ajax() should not be empty!!" ); }}

assertEmpty(mixed $actual[, string $message = ''])

• Prototype

Assertion Message - Output

• Output1) EqualTest::test_AssertMessagegenerate_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 Codeclass 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 Codeclass 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 testtests|── _log|── bootstrap.php|── handlers│ └── _details│ |── TRConverterTest.php│ └── tmql│ └── TMQLEscapeTest.php|── lib└── phpunit.xml

middleware_rev |── handlers│ └── _details│ |── TRConverter.class.php│ └── tmql│ └── TMQLEscape.class.php└── lib

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