Stub you!

43
Stub you! Andrea Giuliano @bit_shark mercoledì 26 giugno 13

description

Phake mocking object presentation to PUG Rome

Transcript of Stub you!

Stub you!

Andrea Giuliano@bit_shark

mercoledì 26 giugno 13

When you’re doing testingyou’re focusing on one element at a time

Unit testing

mercoledì 26 giugno 13

to make a single unit workyou often need other units

The problem is

mercoledì 26 giugno 13

provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test

Stub objects

mercoledì 26 giugno 13

objects pre-programmed with expectationswhich form a specification of the calls

they are expected to receive

Mock objects

mercoledì 26 giugno 13

Mock objectsBehaviour verification

State verificationStub objects

mercoledì 26 giugno 13

A simple stub example

public interface MailService {

public function send(Message $msg);

}

public class MailServiceStub implements MailService {

private $sent = 0;

public function send(Message $msg) {

/*I’m just sent the message */

++$sent;

}

public function numberSent() {

return $this->sent;

}

}

implementation

mercoledì 26 giugno 13

A simple stub example

state verification

class OrderStateTester{...

public function testOrderSendsMailIfFilled() {

$order = new Order(TALISKER, 51);

$mailer = new MailServiceStub();

$order->setMailer($mailer);

$order->fill(/*somestuff*/);

$this->assertEquals(1, $mailer->numberSent());

}

}

mercoledì 26 giugno 13

We’ve wrote a simple test.We’ve tested only one message has been sent

BUT

We’ve not tested it was sent to the right person with right content etc.

mercoledì 26 giugno 13

...using mock object

class OrderInteractionTester...

public function testOrderSendsMailIfFilled() {

$order = new Order(TALISKER, 51);

$warehouse = $this->mock(“Warehouse”);

$mailer = $this->mock(“MailService”);

$order->setMailer($mailer);

$mailer->expects(once())->method("send");

$warehouse->expects(once())->method("hasInventory")

->withAnyArguments()

->will(returnValue(false));

$order->fill($warehouse->proxy());

}

}

mercoledì 26 giugno 13

PHAKEPHP MOCKING FRAMEWORK

mercoledì 26 giugno 13

Zero-config

Phake was designed with PHPUnit in mind

mercoledì 26 giugno 13

stub a method

$stub = $this->getMock('Namespace\MyClass');

$stub->expects($this->any())

->method('doSomething')

->with('param')

->will($this->returnValue('returned'));

with PHPUnit

mercoledì 26 giugno 13

stub a method

$stub = $this->getMock('Namespace\MyClass');

$stub->expects($this->any())

->method('doSomething')

->with('param')

->will($this->returnValue('returned'));

$stub = Phake::mock('Namespace\MyClass');

Phake::when($stub)->doSomething('param')->thenReturn('returned');

with PHPUnit

with Phake

mercoledì 26 giugno 13

an exampleclass ShoppingCartTest extends PHPUnit_Framework_TestCase{! private $shoppingCart;! private $item1;! private $item2;! private $item3;

! public function setUp()! {! ! $this->item1 = Phake::mock('Item');! ! $this->item2 = Phake::mock('Item');! ! $this->item3 = Phake::mock('Item');

! ! Phake::when($this->item1)->getPrice()->thenReturn(100);! ! Phake::when($this->item2)->getPrice()->thenReturn(200);! ! Phake::when($this->item3)->getPrice()->thenReturn(300);

! ! $this->shoppingCart = new ShoppingCart();! ! $this->shoppingCart->addItem($this->item1);! ! $this->shoppingCart->addItem($this->item2);! ! $this->shoppingCart->addItem($this->item3);! }

! public function testGetSub()! {! ! $this->assertEquals(600, $this->shoppingCart->getSubTotal());! }}

mercoledì 26 giugno 13

ShoppingCart implementation

class ShoppingCart

{

! /**

! * Returns the current sub total of the customer's order

! * @return money

! */

! public function getSubTotal()

! {

! ! $total = 0;

! ! foreach ($this->items as $item)

! ! {

! ! ! $total += $item->getPrice();

! ! }

! ! return $total;

! }

}

mercoledì 26 giugno 13

stubbing multiple calls

$stub = $this->getMock('Namespace\MyClass');

$stub->expects($this->any())

->method('doSomething')

->will($this->returnCallback(function($param) {

$toReturn = array(

'param1' => 'returned1',

'param2' => 'returned2',

}));

with PHPUnit

mercoledì 26 giugno 13

stubbing multiple calls

$stub = $this->getMock('Namespace\MyClass');

$stub->expects($this->any())

->method('doSomething')

->will($this->returnCallback(function($param) {

$toReturn = array(

'param1' => 'returned1',

'param2' => 'returned2',

}));

$stub = Phake::mock('Namespace\MyClass');

Phake::when($stub)->doSomething('param1')->thenReturn('returned1')

Phake::when($stub)->doSomething('param2')->thenReturn('returned2');

with PHPUnit

with Phake

mercoledì 26 giugno 13

an exampleclass ItemGroupTest extends PHPUnit_Framework_TestCase{! private $itemGroup;! private $item1;! private $item2;! private $item3;

! public function setUp()! {! ! $this->item1 = Phake::mock('Item');! ! $this->item2 = Phake::mock('Item');! ! $this->item3 = Phake::mock('Item');

! ! $this->itemGroup = new ItemGroup(array($this->item1, $this->item2, $this->item3));! }

! public function testAddItemsToCart()! {! ! $cart = Phake::mock('ShoppingCart');! ! Phake::when($cart)->addItem($this->item1)->thenReturn(10);! ! Phake::when($cart)->addItem($this->item2)->thenReturn(20);! ! Phake::when($cart)->addItem($this->item3)->thenReturn(30);

! ! $totalCost = $this->itemGroup->addItemsToCart($cart);! ! $this->assertEquals(60, $totalCost);! }}

mercoledì 26 giugno 13

an example with consecutive callsclass ItemGroupTest extends PHPUnit_Framework_TestCase{! private $itemGroup;! private $item1;! private $item2;! private $item3;

! public function setUp()! {! ! $this->item1 = Phake::mock('Item');! ! $this->item2 = Phake::mock('Item');! ! $this->item3 = Phake::mock('Item');

! ! $this->itemGroup = new ItemGroup(array($this->item1, $this->item2, $this->item3));! }

! public function testAddItemsToCart()! {! ! $cart = Phake::mock('ShoppingCart');! ! Phake::when($cart)->addItem(Phake::anyParameters())->thenReturn(10)! ! ! ->thenReturn(20)! ! ! ->thenReturn(30);

! ! $totalCost = $this->itemGroup->addItemsToCart($cart);! ! $this->assertEquals(30, $totalCost);! }}

mercoledì 26 giugno 13

partial mockclass MyClass{! private $value;

! public __construct($value)! {! ! $this->value = $value;! }

! public function foo()! {! ! return $this->value;! }}

mercoledì 26 giugno 13

partial mockclass MyClass{! private $value;

! public __construct($value)! {! ! $this->value = $value;! }

! public function foo()! {! ! return $this->value;! }}

class MyClassTest extends PHPUnit_Framework_TestCase{! public function testCallingParent()! {! ! $mock = Phake::partialMock('MyClass', 42);

! ! $this->assertEquals(42, $mock->foo());! }}

mercoledì 26 giugno 13

default stub

class MyClassTest extends PHPUnit_Framework_TestCase

{

! public function testDefaultStubs()

! {

! ! $mock = Phake::mock('MyClass', Phake::ifUnstubbed()->thenReturn(42));

! ! $this->assertEquals(42, $mock->foo());

! }

}

class MyClass{! private $value;

! public __construct($value)! {! ! $this->value = $value;! }

! public function foo()! {! ! return $this->value;! }}

mercoledì 26 giugno 13

stubbing magic methods

class MagicClass{ public function __call($method, $args)

{return '__call';

}}

mercoledì 26 giugno 13

stubbing magic methods

class MagicClass{ public function __call($method, $args)

{return '__call';

}}

class MagicClassTest extends PHPUnit_Framework_TestCase{ public function testMagicCall() { $mock = Phake::mock('MagicClass');

Phake::when($mock)->myMethod()->thenReturn(42);

$this->assertEquals(42, $mock->myMethod()); }}

MyMethod is handled by __call() method

mercoledì 26 giugno 13

verify invocations

class MyTest extends PHPUnit_Framework_TestCase

{

public function testPHPUnitMock()

{

$mock = $this->getMock('PhakeTest_MockedClass');

$mock->expects($this->once())->method('fooWithArgument')

->with('foo');

$mock->expects($this->once())->method('fooWithArgument')

->with('bar');

$mock->fooWithArgument('foo');

$mock->fooWithArgument('bar');

}

}

with PHPUnit - BAD!

mercoledì 26 giugno 13

verify invocations

class MyTest extends PHPUnit_Framework_TestCase

{

public function testPHPUnitMock()

{

$mock = $this->getMock('PhakeTest_MockedClass');

$mock->expects($this->once())->method('fooWithArgument')

->with('foo');

$mock->expects($this->once())->method('fooWithArgument')

->with('bar');

$mock->fooWithArgument('foo');

$mock->fooWithArgument('bar');

}

}

with PHPUnit - BAD!

//I’m failing, with you!

mercoledì 26 giugno 13

verify invocations

class MyTest extends PHPUnit_Framework_TestCase

{

public function testPHPUnitMock()

{

$mock = $this->getMock('PhakeTest_MockedClass');

$mock->expects($this->at(0))->method('fooWithArgument')

->with('foo');

$mock->expects($this->at(1))->method('fooWithArgument')

->with('bar');

$mock->fooWithArgument('foo');

$mock->fooWithArgument('bar');

}

}

with PHPUnit - BETTER

mercoledì 26 giugno 13

verify invocations

class MyTest extends PHPUnit_Framework_TestCase

{

public function testPHPUnitMock()

{

$mock = Phake::mock('PhakeTest_MockedClass');

$mock->fooWithArgument('foo');

$mock->fooWithArgument('bar');

Phake::verify($mock)->fooWithArgument('foo');

Phake::verify($mock)->fooWithArgument('bar');

}

}

with Phake

mercoledì 26 giugno 13

verify invocations

class MyTest extends PHPUnit_Framework_TestCase

{

public function testPHPUnitMock()

{

$mock = Phake::mock('PhakeTest_MockedClass');

$mock->fooWithArgument('foo');

$mock->fooWithArgument('foo');

Phake::verify($mock, Phake::times(2))->fooWithArgument('foo');

}

}

with Phake - multiple invocation

Phake::times($n)Phake::atLeast($n)Phake::atMost($n)

options:

mercoledì 26 giugno 13

verify invocations in order

class MyTest extends PHPUnit_Framework_TestCase

{

public function testPHPUnitMock()

{

$mock = Phake::mock('PhakeTest_MockedClass');

$mock->fooWithArgument('foo');

$mock->fooWithArgument('bar');

Phake::inOrder(

Phake::verify($mock)->fooWithArgument('foo'),

Phake::verify($mock)->fooWithArgument('bar')

);

}

}

mercoledì 26 giugno 13

verify no interaction

class MyTest extends PHPUnit_Framework_TestCase{ public function testPHPUnitNoInteraction() { $mock = $this->getMock('PhakeTestCase_MockedClass'); $mock->expects($this->never()) ->method('foo');

//.... }}

with PHPUnit

mercoledì 26 giugno 13

verify no interaction

class MyTest extends PHPUnit_Framework_TestCase{ public function testPHPUnitNoInteraction() { $mock = $this->getMock('PhakeTestCase_MockedClass'); $mock->expects($this->never()) ->method('foo');

//.... }}

with PHPUnit

class MyTest extends PHPUnit_Framework_TestCase{ public function testPhakeNoInteraction() { $mock = Phake::mock('PhakeTestCase_MockedClass'); //... Phake::verifyNoInteractions($mock); }}

with Phake

mercoledì 26 giugno 13

verify magic calls

class MagicClass{ public function __call($method, $args) { return '__call'; }}

mercoledì 26 giugno 13

verify magic calls

class MagicClass{ public function __call($method, $args) { return '__call'; }}

class MagicClassTest extends PHPUnit_Framework_TestCase

{

public function testMagicCall()

{

$mock = Phake::mock('MagicClass');

$mock->myMethod();

Phake::verify($mock)->myMethod();

}

}

mercoledì 26 giugno 13

throwing exception

class MyClass{! private $logger;!! public function __construct(Logger $logger)! {! ! $this->logger = $logger;! }

! public function processSomeData(MyDataProcessor $processor, MyData $data)! {! ! try {! ! ! $processor->process($data);! ! }! ! catch (Exception $e) {! ! ! $this->logger->log($e->getMessage());! ! }! }}

Suppose you have a class that logs a message with the exception message

mercoledì 26 giugno 13

throwing exception

class MyClassTest extends PHPUnit_Framework_TestCase{! public function testProcessSomeDataLogsExceptions()! {! ! $logger = Phake::mock('Logger');! ! $data = Phake::mock('MyData');! ! $processor = Phake::mock('MyDataProcessor');! !

Suppose you have a class that logs a message with the exception message

mercoledì 26 giugno 13

throwing exception

class MyClassTest extends PHPUnit_Framework_TestCase{! public function testProcessSomeDataLogsExceptions()! {! ! $logger = Phake::mock('Logger');! ! $data = Phake::mock('MyData');! ! $processor = Phake::mock('MyDataProcessor');! !

Suppose you have a class that logs a message with the exception message

! ! Phake::when($processor)->process($data) ->thenThrow(new Exception('My error message!');

mercoledì 26 giugno 13

throwing exception

class MyClassTest extends PHPUnit_Framework_TestCase{! public function testProcessSomeDataLogsExceptions()! {! ! $logger = Phake::mock('Logger');! ! $data = Phake::mock('MyData');! ! $processor = Phake::mock('MyDataProcessor');! !

Suppose you have a class that logs a message with the exception message

! ! Phake::when($processor)->process($data) ->thenThrow(new Exception('My error message!');

! ! $sut = new MyClass($logger);! ! $sut->processSomeData($processor, $data);

mercoledì 26 giugno 13

throwing exception

class MyClassTest extends PHPUnit_Framework_TestCase{! public function testProcessSomeDataLogsExceptions()! {! ! $logger = Phake::mock('Logger');! ! $data = Phake::mock('MyData');! ! $processor = Phake::mock('MyDataProcessor');! !

Suppose you have a class that logs a message with the exception message

! ! Phake::when($processor)->process($data) ->thenThrow(new Exception('My error message!');

! ! $sut = new MyClass($logger);! ! $sut->processSomeData($processor, $data);

! ! Phake::verify($logger)->log('My error message!');! }}

mercoledì 26 giugno 13

parameter capturinginterface CardCollection{ public function getNumberOfCards();}

class MyPokerGameTest extends PHPUnit_Framework_TestCase{ public function testDealCards() { $dealer = Phake::partialMock('MyPokerDealer'); $players = Phake::mock('PlayerCollection');

$cardGame = new MyPokerGame($dealer, $players);

Phake::verify($dealer)->deal(Phake::capture($deck), $players);

$this->assertEquals(52, $deck->getNumberOfCards()); }}

Class MyPokerDealer{ public function getNumberOfCards(){ .. .. $dealer->deal($deck, $players) }}

mercoledì 26 giugno 13

Phake

pear channel-discover pear.digitalsandwich.compear install digitalsandwich/Phake

Available via pear

Available via Composer

"phake/phake": "dev-master"

mercoledì 26 giugno 13

Questions?

Thanks!

Andrea Giuliano@bit_shark

mercoledì 26 giugno 13