Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stub you!

Stub you!

Phake mocking object presentation to PUG Rome

Andrea Giuliano

June 25, 2013
Tweet

More Decks by Andrea Giuliano

Other Decks in Programming

Transcript

  1. When you’re doing testing you’re focusing on one element at

    a time Unit testing mercoledì 26 giugno 13
  2. to make a single unit work you often need other

    units The problem is mercoledì 26 giugno 13
  3. 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
  4. objects pre-programmed with expectations which form a specification of the

    calls they are expected to receive Mock objects mercoledì 26 giugno 13
  5. 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
  6. 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
  7. 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
  8. ...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
  9. 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
  10. an example class 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
  11. 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
  12. 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
  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
  14. an example class 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
  15. an example with consecutive calls class 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
  16. partial mock class MyClass { ! private $value; ! public

    __construct($value) ! { ! ! $this->value = $value; ! } ! public function foo() ! { ! ! return $this->value; ! } } mercoledì 26 giugno 13
  17. partial mock class 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. parameter capturing interface 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
  35. Phake pear channel-discover pear.digitalsandwich.com pear install digitalsandwich/Phake Available via pear

    Available via Composer "phake/phake": "dev-master" mercoledì 26 giugno 13