Presentation showing that writing tests is not really hard with examples on testing a simple class, testing with dataproviders, fixtures, mocks, stubs, databases and how to use zend framework bootstrap for MVC testing.
Given at php|tek 09 unconf sessions.
2. Who am I ?
Michelangelo van Dam
Independent Enterprise PHP consultant
Co-founder PHPBelgium
Mail me at dragonbe [at] gmail [dot] com
Follow me on http://twitter.com/DragonBe
Read my articles on http://dragonbe.com
See my profile on http://linkedin.com/in/michelangelovandam
2
3. Unit testing ?
• test smallest piece of code (unit)
• to verify its behavior is as expected
• exceptions are thrown
• code remains backwards compatible
3
4. Everyone should test !
• rule #1: you should test
• rule #2: see rule #1 (no excuses!!!)
• Why?
- tests are automated
- run over and over again
- detect flaws, errors, mistakes, screw ups
- progress indication of the project
4
5. Why PHPUnit ?
• part of xUnit family
• ported by Sebastian Bergmann
• PEAR package
- pear channel-discover pear.phpunit.de
- pear install phpunit/PHPUnit
5
6. <?php
My first class
class SayHello
{
public $name;
public function __construct($name = 'nobody')
{
$this->name = $name;
}
public function speak()
{
return quot;Hello {$this->name}!quot;;
}
}
6
7. <?php
Run it
require_once 'sayhello.php';
$hello = new SayHello();
echo $hello->speak();
// outputs Hello nobody!
7
8. <?php
Test it !
require_once 'sayhello.php';
class SayHelloTest extends PHPUnit_Framework_TestCase
{
public function testSpeakWithoutParams ()
{
$hello = new SayHello();
$this->assertEquals(quot;Hello nobody!quot;, $hello->speak());
}
public function testSpeakWithParams ()
{
$hello = new SayHello('Marco');
$this->assertEquals(quot;Hello Marco!quot;, $hello->speak());
}
}
8
9. We’re good
$ phpunit SayHelloTest
PHPUnit 3.3.15 by Sebastian Bergmann.
..
Time: 0 seconds
OK (2 tests, 2 assertions)
9
14. <?php
CombineTest
class CombineTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider provider
*/
public function testCombine($a, $b, $c)
{
$this->assertEquals($c, $a . ' ' . $b);
}
public function provider()
{
return array (
array ('Hello','World','Hello World'),
array ('Go','PHP','Go PHP'),
array ('This','Fails','This succeeds')
);
}
}
14
15. Testing CombineTest
# phpunit CombineTest CombineTest.php
PHPUnit 3.3.2 by Sebastian Bergmann.
..F
Time: 0 seconds
There was 1 failure:
1) testCombine(CombineTest) with data set #2 ('This', 'Fails',
'This succeeds')
Failed asserting that two strings are equal.
expected string <This succeeds>
difference < xxxxx???>
got string <This Fails>
/root/dev/phpunittutorial/CombineTest.php:9
FAILURES!
Tests: 3, Assertions: 3, Failures: 1.
15
16. Expected Exception
• testing exceptions
- that they are thrown
- are properly catched
16
17. OopsTest
<?php
class OopsTest extends PHPUnit_Framework_TestCase
{
public function testOops()
{
try {
throw new Exception('I just made a booboo');
} catch (Exception $expected) {
return;
}
$this->fail('An expected Exception was not thrown');
}
}
17
18. Testing OopsTest
# phpunit OopsTest OopsTest.php
PHPUnit 3.3.2 by Sebastian Bergmann.
.
Time: 0 seconds
OK (1 test, 0 assertions)
18
19. Fixtures
• is a “known state” of an application
- needs to be ‘set up’ at start of test
- needs to be ‘torn down’ at end of test
- shares “states” over test methods
19
20. <?php
FixmeTest
class FixmeTest extends PHPUnit_Framework_TestCase
{
protected $fixme;
public function setUp()
{
$this->fixme = array ();
}
public function testFixmeEmpty()
{
$this->assertEquals(0, sizeof($this->fixme));
}
public function testFixmeHasOne()
{
array_push($this->fixme, 'element');
$this->assertEquals(1, sizeof($this->fixme));
}
}
20
21. Testing FixmeTest
# phpunit FixmeTest FixmeTest.php
PHPUnit 3.3.2 by Sebastian Bergmann.
..
Time: 0 seconds
OK (2 tests, 2 assertions)
21
23. Stubs
• isolates tests from external influences
- slow connections
- expensive and complex resources
• replaces a “system under test” (SUT)
- for the purpose of testing
23
24. StubTest
<?php
// example taken from phpunit.de
class StubTest extends PHPUnit_Framework_TestCase
{
public function testStub()
{
$stub = $this->getMock('SomeClass');
$stub->expects($this->any())
->method('doSometing')
->will($this->returnValue('foo'));
}
// Calling $stub->doSomething() will now return 'foo'
}
24
25. Testing StubTest
# phpunit StubTest StubTest.php
PHPUnit 3.3.2 by Sebastian Bergmann.
.
Time: 0 seconds
OK (1 test, 1 assertion)
25
26. Mocks
• simulated objects
• mimics API or behaviour
• in a controlled way
• to test a real object
26
27. <?php
ObserverTest
// example taken from Sebastian Bergmann’s slides on
// slideshare.net/sebastian_bergmann/advanced-phpunit-topics
class ObserverTest extends PHPUnit_Framework_TestCase
{
public function testUpdateIsCalledOnce()
{
$observer = $this->getMock('Observer', array('update'));
$observer->expects($this->once())
->method('update')
->with($this->equalTo('something'));
$subject = new Subject;
$subject->attach($observer)
->doSomething();
}
}
27
28. Database Testing
• ported by Mike Lively from DBUnit
• PHPUnit_Extensions_Database_TestCase
• for database-driven projects
• puts DB in know state between tests
• imports and exports DB data from/to XML
• easily added to existing tests
28
29. BankAccount Example
BankAccount example by Mike Lively
http://www.ds-o.com/archives/63-PHPUnit-Database-Extension-DBUnit-Port.html
http://www.ds-o.com/archives/64-Adding-Database-Tests-to-Existing-PHPUnit-Test-Cases.html
BankAccount Classs by Sebastian Bergmann
http://www.slideshare.net/sebastian_bergmann/testing-phpweb-applications-with-phpunit-and-selenium
The full BankAccount class
http://www.phpunit.de/browser/phpunit/branches/release/3.3/PHPUnit/Samples/BankAccountDB/
BankAccount.php
29
30. Database Testing Example
<?php
require_once 'PHPUnit/Extensions/Database/Tescase.php';
require_once 'PHPUnit/Extensions/Database/Dataset/FlatXmlDataSet.php';
require_once 'BankAccount.php';
class BankAccountDBTest extends PHPUnit_Extensions_Database_Testcase
{
public function getConnection()
{
$pdo = new PDO('sqlite::memory:');
return $this->createDefaultDBConnection($pdo, 'testdb');
}
public function getDataSet()
{
return $this->createFlatFileXMLDataSet(
dirname(__FILE__) . '/_files/dataset.xml');
}
...
30
31. Testing data store/retrieve
...
public function testNewAccountCreation()
{
$bank_account = new BankAccount('12345678912345678', $this->pdo);
$xml_dataset = $this->createFlatXMLDataSet(
dirname(__FILE__).'/_files/ba-new-account.xml');
$this->assertDataSetsEquals($xml_dataset,
$this->getConnection()->createDataSet());
}
...
31
32. Testing BankAccount
$ phpunit BankAccountDBTestPHPUnit 3.3.15 by Sebastian Bergmann.
.....
Time: 0 seconds
OK (5 tests, 10 assertions)
32
38. Interesting Readings
• PHPUnit by Sebastian Bergmann
http://phpunit.de
• Art of Unit Testing by Roy Osherove
http://artofunittesting.com
• Mike Lively’s blog
http://www.ds-o.com/archives/63-PHPUnit-Database-Extension-DBUnit-Port.html
38
39. Credits
Lomo elePHPant
http://www.flickr.com/photos/jakobwesthoff/3231273333/
Sebastian Bergmann
http://www.flickr.com/photos/sebastian_bergmann/2293021853
39
41. License
This presentation is released under the Creative Commons
Attribution-Share Alike 3.0 Unported License
You are free:
- to share : to copy, distribute and transmit the work
- to remix : to adapt the work
Under the following conditions:
- attribution :You must attribute the work in the manner specified by the author or licensor
- share alike : If you alter, transform, or build upon this work, you may distribute the resulting work
only under the same, similar or a compatible license
See: http://creativecommons.org/licenses/by-sa/3.0/
41
42. Questions ?
Thank you
This presentation will be available on
http://slideshare.com/DragonBe
Vote this talk
http://joind.in/442
42