Share
Embed
Transcript
54 slides
-
Test runner Test case Test suites Test fixtures Test execution
XUnit architecture -
XUnit / Test execution setUp(); tearDown(); Create our isolated “world”
for the current test Body of the test - all the tests run here Clean up our “world” -
Example <?php class PlayerTest extends PHPUnit_Framework_TestCase { public function testTakesDamage()
{ $player = new Player([‘health’ => 100]); $player->takesDamage(17); $this->assertEquals(83, $player->health()); } } -
Put tests for Class in ClassTest ClassTest inherits from PHPUnit_Framework_TestCase
Test methods are public and named test* Conventions -
Fixtures class PlayerTest extends PHPUnit_Framework_TestCase { protected $player; protected function
setUp() { $this->player = new Player([‘health’ => 100]); } public function testTakesTinyDamage() { $player->takesDamage(5); $this->assertEquals(95, $player->health()); } public function testTakesHugeDamage() { $player->takesDamage(60); $this->assertEquals(40, $player->health()); } } -
There are tons! Assertions https://phpunit.de/manual/current/en/appendixes.assertions.html assertEquals assertContains assertInstanceOf assertTrue assertFalse
assertNull assertLessThan assertXmlFileEqualsXmlFile assertFileExist assertCount assertEmpty assertRegExp assert* -
Feed test methods with various data Good for validation Must
be public Either returns with an array of arrays or returns with an iterable object Data Providers -
Data Providers class PlayerTest extends PHPUnit_Framework_TestCase { /** * @dataProvider
damageProvider */ public function testTakesDamage($damage) { $player->takesDamage($damage); $this->assertEquals(100 - $damage, $player->health()); } public function damageProvider() { return [ [44], [23], [76] ]; } } -
If a method generates output For example with print or
echo PHPUnit uses Output Buffering Testing output -
Testing output class PlayerTest extends PHPUnit_Framework_TestCase { public function testPrintName()
{ $this->expectOutputString(‘Crash’); $player->printName(); } } -
Testing exceptions class PlayerTest extends PHPUnit_Framework_TestCase { /** * @expectedException
InvalidDamageException */ public function testTakesDamage() { $player->takesDamage(-5); } } -
A test method depends on other(s) Producer which serves return
data Consumer which depends on one or more producers Test dependencies -
Test dependencies class PlayerTest extends PHPUnit_Framework_TestCase { public function testIsPlayerDead()
{ $player = new Player([‘health’ => 0]); $this->assertTrue($player->isDead()); return $player; } /** * @depends testIsPlayerDead * @expectedException DeadPlayerException */ public function testTakesDamageOnDeadPlayer($deadPlayer) { $deadPlayer->takesDamage(10); } } -
We have to write some tests No time So, create
test list Incomplete tests -
class PlayerTest extends PHPUnit_Framework_TestCase { public function testPlayerCreation() { }
public function testPlayerIsDead() { } public function testPlayerTakesDamage() { } } Incomplete tests C orrect? -
class PlayerTest extends PHPUnit_Framework_TestCase { public function testPlayerCreation() { $this->markTestIncomplete(‘Not
implemented yet!’); } } Incomplete tests -
class DatabaseTest extends PHPUnit_Framework_TestCase { protected function setUp() { if
(!extension_loaded(‘mysqli’)) { $this->markTestSkipped(‘Whops MySQLi extension!’); } } } Skipped tests E nvironm ent dependent -
Use specific assertion Name your tests after their intention Keep
tests small Keep tests fast Test behavior (not methods) Best practices -
class DirTest extends PHPUnit_Extensions_Database_TestCase { /** * @var vfsStreamDirectory */
protected $vfsRootDir; protected function setUp() { $this->vfsRootDir = vfsStream::setup(‘root_dir_name’); } public function testDir() { # $this->vfsRootDir->url(); # vfsStream::newDirectory(‘dir_name’, 0444)->at($this->vfsRootDir); } } Filesystem test -
DB tests / Lifecycle Clean Up Set Up Run Test
Verify Tear Down -
class PlayerTest extends PHPUnit_Extensions_Database_TestCase { public function getConnection() { $pdo
= new PDO( $GLOBALS[‘DB_DSN'], $GLOBALS[‘DB_USER'], $GLOBALS[‘DB_PASSWD'] ); return $this->createDefaultDBConnection($pdo, $GLOBALS['DB_DBNAME']); } public function getDataSet() { return $this->createFlatXMLDataSet('player-seed.xml'); } } DB test S chem a? extract to abstract -
<?xml version="1.0" encoding="UTF-8" ?> <phpunit> <php> <var name="DB_DSN" value="mysql:dbname=players;host=localhost" />
<var name="DB_USER" value="user" /> <var name="DB_PASSWD" value="password" /> <var name="DB_DBNAME" value="players" /> </php> </phpunit> DB test / Config phpunit --configuration dev-adam.xml -
Abstraction layer Db content => object structure DB tests /
DataSet, DataTable -
File-based DataSet XML (FLAT-XML, XML, MYSQL-XML) YAML CSV PHP array
Query-based DataSet Composite DataSet DataSet Filter DB tests / Expectations -
<?xml version="1.0" ?> <dataset> <players id="1" name="Crash" health="100" /> <players
id="2" name="Hunter" health="100" /> </dataset> Flat XML DataSet N U L L ? -
class PlayerTest extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { return
$this->createFlatXMLDataSet('players-seed.xml'); } } Flat XML DataSet -
<?xml version="1.0" ?> <dataset> <table name="players"> <column>id</column> <column>name</column> <column>health</column> <row>
<value>1</value> <value>Crash</value> <value>100</value> </row> <row> <value>2</value> <value>Hunter</value> <null /> </row> </table> </dataset> XML DataSet -
class PlayerTest extends PHPUnit_Extensions_Database_TestCase { public function getDataSet() { return
$this-> createXMLDataSet('players-seed.xml'); } } XML DataSet -
class PlayerTest extends PHPUnit_Extensions_Database_TestCase { public function testAddPlayer() { $this->assertEquals(
1, $this->getConnection()->getRowCount('players') ); # add Player to the players db table $this->assertEquals( 2, $this->getConnection()->getRowCount('players') ); } } DB Assertions / RowCount -
class PlayerTest extends PHPUnit_Extensions_Database_TestCase { public function testAddPlayer() { #
add Player to the players db table $queryTable = $this->getConnection()->createQueryTable( 'players', 'SELECT * FROM players' ); $expectedTable = $this ->createFlatXmlDataSet('expectedPlayer.xml') ->getTable('players'); $this->assertTablesEqual($expectedTable, $queryTable); } } DB Assertions / Table State -
$plugin = new Guzzle\Plugin\Mock\MockPlugin(); $plugin->addResponse(new Guzzle\Http\Message\Response(200)); $client = new Guzzle\Http\Client();
$client->addSubscriber($plugin); $request = $client->get('http://www.test.com/'); $request->send(); Mocking API call -
class FooDummy implements Foo { public function bar() { return
null; } } Test Doubles / Dummy -
class PlayerTest extends PHPUnit_Framework_TestCase { public function testHealth() { $stub
= $this ->getMockBuilder('Player') ->getMock(); $stub ->method('health') ->willReturn(15); # $stub->health() ~= $player->health() } } Test Doubles / Stub -
class FooStub implements Foo { public function bar() { return
42; } } Test Doubles / Stub -
class Subject { public function doSomething(Observer $observer) { $observer->update(‘something’) }
} Test Doubles / Mock class Observer { public function update($argument) { # … } } -
class SubjectTest extends PHPUnit_Framework_TestCase { public function testObserversAreUpdated() { $observer
= $this ->getMockBuilder('Observer') ->setMethods(array('update')) ->getMock(); $observer ->expects($this->once()) ->method('update') ->with($this->equalTo(‘something')); $subject = new Subject(); $subject->doSomething($observer); } } Test Doubles / Mock -
. |— src | `— foo | `— bar |
`— Baz.php `— tests | — api `— unit `— foo `— bar `— BazTest.php Organazing Tests