introductiontophpunitandbestpractices-090721111610-phpapp02

40
 Introduction to PHPUnit & Best Practices Sebastian Bergmann July 20 t h 2009

Upload: hathanh13

Post on 07-Apr-2018

217 views

Category:

Documents


0 download

TRANSCRIPT

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 1/40

 

Introduction to PHPUnit & Best Practices

Sebastian Bergmann

July 20th 2009

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 2/40

Who I am

Sebastian Bergmann Involved in the PHP

project since 2000

Creator of PHPUnit Co-Founder and

Principal Consultantwith thePHP.cc

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 3/40

The  Bank Account ExampleBankAccount.php<?php

class BankAccount {protected $balance = 0;

public function getBalance() {return $this->balance;

}

protected function setBalance($balance) {

if ($balance >= 0) {  $this->balance = $balance;

} else {throw new RuntimeException;

}}

public function depositMoney($balance) {  $this->setBalance($this->getBalance() + $balance);

}

public function withdrawMoney($balance) {  $this->setBalance($this->getBalance() - $balance);

}}

?>

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 4/40

The  Bank Account Example

<?phprequire_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase {

}

BankAccountTest.php

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 5/40

The  Bank Account ExampleBankAccountTest.php<?phprequire_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase {public function testBalanceIsInitiallyZero() {

}

}

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 6/40

The  Bank Account ExampleBankAccountTest.php<?phprequire_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase {public function testBalanceIsInitiallyZero() {

  $ba = new BankAccount;  $this->assertEquals(0, $ba->getBalance());

return $ba;}

}

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 7/40

The  Bank Account ExampleBankAccountTest.php<?phprequire_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase {public function testBalanceIsInitiallyZero() {

$ba = new BankAccount;$this->assertEquals(0, $ba->getBalance());

return $ba;}

  /*** @depends testBalanceIsInitiallyZero* @expectedException RuntimeException*/public function testBalanceCannotBecomeNegative(BankAccount $ba) {

  $ba->withdrawMoney(1);}

}

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 8/40

The  Bank Account ExampleBankAccountTest.php<?phprequire_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase {public function testBalanceIsInitiallyZero() {

$ba = new BankAccount;$this->assertEquals(0, $ba->getBalance());

return $ba;}

/*** @depends testBalanceIsInitiallyZero* @expectedException RuntimeException*/public function testBalanceCannotBecomeNegative(BankAccount $ba) {

$ba->withdrawMoney(1);}

  /*** @depends testBalanceIsInitiallyZero* @expectedException RuntimeException*/public function testBalanceCannotBecomeNegative2(BankAccount $ba) {

  $ba->depositMoney(-1);}

  // ...}

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 9/40

The  Bank Account ExampleBankAccountTest.php<?phprequire_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase {// ...

  /*** @depends testBalanceIsInitiallyZero*/public function testDepositingMoneyWorks(BankAccount $ba) {

  $ba->depositMoney(1);  $this->assertEquals(1, $ba->getBalance());

return $ba;}

}

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 10/40

The  Bank Account ExampleBankAccountTest.php<?phprequire_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase {// ...

/*** @depends testBalanceIsInitiallyZero*/public function testDepositingMoneyWorks(BankAccount $ba) {

$ba->depositMoney(1);$this->assertEquals(1, $ba->getBalance());

return $ba;}

  /*** @depends testDepositingMoneyWorks*/

public function testWithdrawingMoneyWorks(BankAccount $ba) {  $ba->withdrawMoney(1);  $this->assertEquals(0, $ba->getBalance());

}}

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 11/40

The  Bank Account ExampleRunning tests

sb@ubuntu ~ % phpunit BankAccountTest

PHPUnit 3.4.0 by Sebastian Bergmann.

.....

Time: 0 seconds

OK (5 tests, 5 assertions)

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 12/40

The  Bank Account Example

<?phpclass BankAccount {

protected $balance = 1;

public function getBalance() {return $this->balance;

}

protected function setBalance($balance) {

if ($balance >= 0) {$this->balance = $balance;

} else {throw new RuntimeException;

}}

public function depositMoney($balance) {$this->setBalance($this->getBalance() + $balance);

}

public function withdrawMoney($balance) {$this->setBalance($this->getBalance() - $balance);

}}

?>

BankAccount.php

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 13/40

The  Bank Account ExampleRunning tests

sb@ubuntu ~ % phpunit BankAccountTest

PHPUnit 3.4.0 by Sebastian Bergmann.

FSSSS

Time: 0 seconds

There was 1 failure:

1) BankAccountTest::testBalanceIsInitiallyZeroFailed asserting that <integer:1> matches expected <integer:0>./home/sb/BankAccountTest.php:7

FAILURES!Tests: 1, Assertions: 1, Failures: 1, Skipped: 4.

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 14/40

The  Bank Account ExampleRunning tests

sb@ubuntu ~ % phpunit --verbose BankAccountTestPHPUnit 3.4.0 by Sebastian Bergmann.

FSSSS

Time: 0 seconds

There was 1 failure:

1) BankAccountTest::testBalanceIsInitiallyZeroFailed asserting that <integer:1> matches expected <integer:0>.

/home/sb/BankAccountTest.php:7

There were 4 skipped tests:

1) BankAccountTest::testBalanceCannotBecomeNegativeThis test depends on "BankAccountTest::testBalanceIsInitiallyZero" to pass.

2) BankAccountTest::testBalanceCannotBecomeNegative2This test depends on "BankAccountTest::testBalanceIsInitiallyZero" to pass.

3) BankAccountTest::testDepositingMoneyWorksThis test depends on "BankAccountTest::testBalanceIsInitiallyZero" to pass.

4) BankAccountTest::testWithdrawingMoneyWorksThis test depends on "BankAccountTest::testDepositingMoneyWorks" to pass.

FAILURES!Tests: 1, Assertions: 1, Failures: 1, Skipped: 4.

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 15/40

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 16/40

The  Bank Account ExampleCode Coverage

sb@ubuntu ~ % phpunit --coverage-html /tmp/report BankAccountTest

PHPUnit 3.4.0 by Sebastian Bergmann.

.....

Time: 0 seconds

OK (5 tests, 5 assertions)

Generating code coverage report, this may take a moment.

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 17/40

”The secret in testing isin writing testable code”

Miš

ko Hevery 

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 18/40

Untestable Code

Most people say Make things private

Use final keyword

Write long methods ...

Real issues Mix new with logic

Look for things

Work in constructor Singletons

Global state

Static methods

This slide contains material by Miško Hevery

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 19/40

Untestable Code

<?php

class Document {private $html;

public function __construct($url) {  $client = new HttpClient;  $this->html = $client->get($url);

}}

Mixing object graph

construction with work

Doing work in theconstructor 

This slide contains material by Miško Hevery

Object Graph Construction Issues

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 20/40

<?php

class Document {private $html;

public function __construct(HttpClient $client, $url) {  $this->html = $client->get($url);

}

}

Doing work in theconstructor 

This slide contains material by Miško Hevery

Untestable CodeObject Graph Construction Issues

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 21/40

<?php

class DocumentFactory {public function __construct(HttpClient $client) {  $this->client = $client;

}

public function build($url) {

return new Document($this->client->get($url));}}

This slide contains material by Miško Hevery

Untestable CodeDecouple object graph construction from other work 

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 22/40

<?php

class DocumentFactory {public function __construct(HttpClient $client) {$this->client = $client;

}

public function build($url) {

return new Document($this->client->get($url));}}

class Document {private $html;

public function __construct($html) {  $this->html = $html;

}}

This slide contains material by Miško Hevery

Untestable CodeDecouple object graph construction from other work 

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 23/40

Untestable CodeGlobal State

Global variables Static variables in classes

Static variables in functions and methods

Persistent Data Database

Filesystem

Distributed Data memcached

bl d

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 24/40

Untestable CodeGlobal Variables in PHP

A variable $foo declared in the global scopeis stored in $GLOBALS['foo'].

$GLOBALS is a so-called superglobal.

Superglobals are built-in variables that areavailable in all scopes.

$GLOBALS['foo'] can be used to access theglobal variable $foo in the scope of a

function or method global $foo; can be used to create a local

variable with a reference to the globalvariable.

bl d

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 25/40

Untestable CodeGlobal Variables in PHP

$GLOBALS = array(  'GLOBALS' => &$GLOBALS,  '_ENV' => array(),  '_POST' => array(),

  '_GET' => array(),  '_COOKIE' => array(),  '_SERVER' => array(),  '_FILES' => array(),

  '_REQUEST' => array());

bl d

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 26/40

Untestable CodePHPUnit and Global Variables

Default Behaviour Backup $GLOBALS before the execution of a test

Run the test

Restore $GLOBALS after the execution of a test

$backupGlobals = FALSE Do not backup and restore $GLOBALS

$backupGlobalsBlacklist = array() Backup and restore $GLOBALS but not the

variables listed in the array

bl C d

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 27/40

Untestable CodePHPUnit and Static Attributes

Default Behaviour

Backup static attributes of user-defined classes before theexecution of a test

Run the test

Restore static attributes of user-defined classes after theexecution of a test

$backupStaticAttributes = FALSE

Do not backup and restore static attributes of user-

defined classes

$backupStaticAttributesBlacklist = array()

Backup and restore static attributes of user-defined

classes but not the variables listed in the array

U bl C d

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 28/40

This slide contains material by Miško Hevery

Untestable CodeHidden Global State

Loaded source files Loaded classes and functions

Sent HTTP headers

new DateTime()

time()

mktime()

rand()

T bl C d

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 29/40

Testable Code

Few Branches More branches increase complexity

Higher complexity leads to more errors

Higher complexity makes testing harder

Few Dependencies

Improved isolation for defect localization Should be injectable to be replacable by

test-specific equivalents

Selected Aspects of Testability 

PHPU i B P i

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 30/40

PHPUnit Best PracticesUse an XML Configuration File

<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"  backupStaticAttributes="false"  syntaxCheck="false"></phpunit>

PHPU i B P i

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 31/40

PHPUnit Best PracticesUse an XML Configuration File

<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"backupStaticAttributes="false"syntaxCheck="false">

<testsuites><testsuite name="My Test Suite"><directory>path/to/dir</directory>

</testsuite></testsuites>

</phpunit>

PHPU i B P i

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 32/40

PHPUnit Best PracticesUse an XML Configuration File

<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"backupStaticAttributes="false"syntaxCheck="false">

<php><const name="foo" value="bar"/><var name="foo" value="bar"/><ini name="foo" value="bar"/>

</php></phpunit>

PHPU i B P i

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 33/40

PHPUnit Best PracticesUse Code Coverage Whitelisting

<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"backupStaticAttributes="false"syntaxCheck="false">

<filter><whitelist addUncoveredFilesFromWhitelist="true"><directory suffix=".php">path/to/dir</directory>

</whitelist></filter>

</phpunit>

PHPU it B t P ti

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 34/40

PHPUnit Best PracticesMake the Code Coverage information more meaningful

/**

* @covers Object_Freezer::freeze*/public function testFreezingAnObjectWorks(){  $this->assertEquals(

array( /* ... */ ),  $this->freezer->freeze(new A(1, 2, 3))

);}

PHPU it B t P ti

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 35/40

PHPUnit Best PracticesExploit dependencies between tests

/**

* @covers Object_Freezer::freeze* @covers Object_Freezer::thaw* @depends testFreezingAnObjectWorks*/public function testFreezingAndThawingAnObjectWorks(){  $object = new A(1, 2, 3);

  $this->assertEquals(  $object,  $this->freezer->thaw(

  $this->freezer->freeze($object))

);}

PHPU it B t P ti

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 36/40

Decouple test code from complex / large sets of test data

<?phpclass DataTest extends PHPUnit_Framework_TestCase{  /**

* @dataProvider providerMethod*/public function testAdd($a, $b, $c){

  $this->assertEquals($c, $a + $b);}

public function providerMethod(){

return array(array(0, 0, 0),

array(0, 1, 1),array(1, 1, 3),array(1, 0, 1)

);}

}

PHPUnit Best Practices

PHPU it B t P ti

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 37/40

PHPUnit Best Practices

sb@ubuntu ~ % phpunit DataTestPHPUnit 3.4.0 by Sebastian Bergmann.

..F.

Time: 0 seconds

There was 1 failure:

1) testAdd(DataTest) with data (1, 1, 3)Failed asserting that <integer:2> matches expected value <integer:3>./home/sb/DataTest.php:19

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

Decouple test code from complex / large sets of test data

PHPU it B st Pr ti s

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 38/40

PHPUnit Best PracticesWrite code that allows you to disable PHPUnit features

Syntax Check Backup/Restore of global variables

Backup/Restore of static attributes

The End

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 39/40

The End

 Thank you for your interest!

 These slides will be posted on

http://slideshare.net/sebastian_bergmann

License

8/6/2019 introductiontophpunitandbestpractices-090721111610-phpapp02

http://slidepdf.com/reader/full/introductiontophpunitandbestpractices-090721111610-phpapp02 40/40

License

   This presentation material is published under the Attribution-Share Alike 3.0 Unportedlicense.

   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 orlicensor (but not in any way that suggests that they endorse you or your use of thework).

● Share Alike. If you alter, transform, or build upon this work, you may distribute theresulting work only under the same, similar or a compatible license.

  For any reuse or distribution, you must make clear to others the license terms of this

work.  Any of the above conditions can be waived if you get permission from the copyright

holder.

  Nothing in this license impairs or restricts the author's moral rights.