Practical approach for testing your software with php unit

Preview:

DESCRIPTION

Practical approach for testing software using PHPUnit

Citation preview

Practical Approach for testing your software with PHPUnit

Mario Bittencourt

Agenda

Current situation

PHPUnit as part of the solution

Best Practices

Current situation

Complexity, dependencies and pace over time

Software Development Life Cycle

Testing

Tedious

Can only cover a small subset of the use cases

Takes a lot of time

Change the code to test it

Automation is key

Goal is to create tests that can be executed automatically to verify that the expected behaviour is met

PHPUnit as part of the solution

Example

class Account

{

protected $balance;

public function __construct($initialAmount)

{

$this->balance = $initialAmount;

}

public function getBalance()

{

return $this->balance;

}

public function deposit($amount)

{

$this->balance += $amount;

}

}

Unit Test

class AccountTest extends PHpUnit_Framework_TestCase{

public function testDepositIncreasesBalance(){

$account = new Account(10);$account->deposit(1);$this->assertEquals(11, $account->getBalance());

}}

Unit Test

PHPUnit 4.3.1 by Sebastian Bergmann.

Account

[x] Deposit increases balance

Best Practices

Give meaningful names to your tests

public function testDeposit() {

$account = new Account(10);$account->deposit(1);$this->assertEquals(

11,$account->getBalance()

);}

DON’T

Give meaningful names to your tests

PHPUnit 4.3.1 by Sebastian Bergmann.

Account

[ ] Deposit

Give meaningful names to your tests

public function testDepositIncreaseBalance() {

$account = new Account(10);$account->deposit(1);$this->assertEquals(

11,$account->getBalance()

);}

Use the most specific assertion available

public function testDepositIncreasesBalance(){

$account = new Account(10);$account->deposit(10);$this->assertGreaterThan(

10,$account->getBalance());

}

DON’T

Use the most specific assertion available

public function testDepositIncreasesBalance(){

$account = new Account(10);$account->deposit(10);$this->assertEquals(20, $account->getBalance());

}

Use the most specific assertion available

public function deposit($amount){

$this->balance += $amount;

if ($amount >= 10) {$this->balance += 1;

}

return true;}

Use the most specific assertion available

There was 1 failure:

1) AccountTest::testDepositIncreasesBalanceFailed asserting that 21 matches expected 20.

AccountTest.php:14

Don’t forget about protectedmethods

class Account{

public function deposit($amount){

if ($this->validateAmount($amount)) {$this->balance += $amount;

} else {throw new Exception('Invalid amount');

}}

}

Don't forget about your protected methods

protected function validateAmount($amount){

if ($amount < 0) {return false;

} else {return true;

}}

Don't forget about your protected methods

public function testValidateAmountWithNegativeIsFalse(){

$account = new Account(10);$this->assertFalse($account->validateAmount(-1));

}

Fatal error: Call to protected method Account::validateAmount()

Don't forget about your protected methods

public function testValidateAmountWithNegativeIsFalse(){

$account = new Account(10);$reflection = new ReflectionObject($account);$method = $reflection->getMethod('validateAmount');$method->setAccessible(true);$this->assertFalse(

$method->invokeArgs($account, array(-1)));

}

Don't forget about your protected methods

PHPUnit 4.3.1 by Sebastian Bergmann.

Account

[x] Deposit increases balance

[x] Validate amount with negative is false

Test just one thing at a time

public function deposit($amount){

if ($amount == 0) {return false;

}$this->balance += $amount;return true;

}

Test just one thing at a time

public function testDepositIncreasesBalance(){

$account = new Account(10);$this->assertTrue($account->deposit(1));$this->assertEquals(11, $account->getBalance());

$this->assertFalse($account->deposit(0));}

Test just one thing at a time

PHPUnit 4.3.1 by Sebastian Bergmann.

.

Time: 35 ms, Memory: 3.00Mb

OK (1 test, 3 assertions)

Test just one thing at a time

public function deposit($amount){

if ($amount < 0) {return false;

}$this->balance += $amount;return true;

}

NEW CRITERIA

Test just one thing at a time

PHPUnit 4.3.1 by Sebastian Bergmann.

There was 1 failure:

1) AccountTest::testDepositIncreasesBalance

Failed asserting that true is false.

AccountTest.php:15

FAILURES!

Tests: 1, Assertions: 3, Failures: 1.

Test just one thing at a time

public function testDepositIncreasesBalance(){$account = new Account(10);$this->assertTrue($account->deposit(1));$this->assertEquals(11, $account->getBalance());}

public function testDepositWithNegativeShouldReturnFalse(){

$account = new Account(10);$this->assertFalse($account->deposit(-1));

}

Test just one thing at a time

PHPUnit 4.3.1 by Sebastian Bergmann.

..

Time: 49 ms, Memory: 3.00Mb

OK (2 tests, 3 assertions)

Use Data Providers for repetitive tests

class Score{

protected $score;

public function update($balance, $numberCc, $amountInLoans)

{// do something ...return $newScore;

}}

Use Data Providers for repetitive tests

class ScoreTest extends PHpUnit_Framework_TestCase{

public function testScoreSomething1(){

$score = new Score(10);$this->assertEquals(300, $score->update(100, 0, 1));

}

public function testScoreSomething2(){

$score = new Score(10);$this->assertEquals(99, $score->update(100, 1, 2));

}}

Use Data Providers for repetitive tests

/*** @dataProvider scoreProvider*/public function testScore($balance, $numberCc, $amountInLoans, $expectedScore){

$score = new Score(10);$this->assertEquals(

$expectedScore, $score->update($balance, $numberCc, $amountInLoans)

);}

Use Data Providers for repetitive tests

public function scoreProvider(){

return array(array(100, 0, 1, 300),array(100, 1, 2, 99),

);}

Use Data Providers for repetitive tests

PHPUnit 4.3.1 by Sebastian Bergmann.

..

Time: 25 ms, Memory: 3.00Mb

OK (2 tests, 2 assertions)

Isolate your test

public function wire($amount, SwiftInterface $targetBank){

if ($targetBank->transfer($amount)) {$this->balance -= $amount;return true;

} else {return false;

}}

Isolate your test

class Hsbc implements SwiftInterface {public function transfer($amount){

// slow method}

}

Isolate your test

public function testWireTransferShouldReturnTrueIfSuccessful(){

$account = new Account(10);$bank = new Hsbc();$this->assertTrue($account->wire(5, $bank));

}

Isolate your test

public function testWireTransferShouldReturnFalseIfFailed(){

// how to fail?$account = new Account(10);$bank = new Hsbc();$this->assertFalse($account->wire(5, $bank));

}

Isolate your test

public function testWireTransferShouldReturnTrueIfSuccessful(){

$account = new Account(10);$bank = $this->getMock(‘Hsbc');$bank->expects($this->any())

->method('transfer')->will($this->returnValue(true));

$this->assertTrue($account->wire(5, $bank));}

Isolate your test

public function testWireTransferShouldReturnFalseIfFailed(){

$account = new Account(10);$bank = $this->getMock('Hsbc');$bank->expects($this->any())

->method('transfer')->will($this->returnValue(false));

$this->assertFalse($account->wire(5, $bank));}

Specify what you are testing with @covers

Use --coverage-html to generate the code coverage report of your code

Specify what you are testing with @covers

Specify what you are testing with @covers

class Exchange {public function convert($amountUsd, $targetCurrency){

// Do some conversion}

}

Specify what you are testing with @covers

public function convertBalance($targetCurrency){

$exchange = new Exchange();return $exchange->convert($this->balance, $targetCurrency);

}

Specify what you are testing with @covers

public function testConvertBalance(){

$account = new Account(10);$this->assertLessThan(

10, $account->convertBalance('GBP'));

}

Specify what you are testing with @covers

Specify what you are testing with @covers

Specify what you are testing with @covers

/**

* @covers Account::convertBalance

*/

public function testConvertBalance(){…

}

Specify what you are testing with @covers

That is all folks!

Contacts

Twitter - @bicatu

Email – mbneto@gmail.com

https://joind.in/12279

http://slidesha.re/1xtcT4y