2. When you’re doing testing
you’re focusing on one element at a time
Unit testing
mercoledì 26 giugno 13
3. to make a single unit work
you often need other units
The problem is
mercoledì 26 giugno 13
4. 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
5. objects pre-programmed with expectations
which form a specification of the calls
they are expected to receive
Mock objects
mercoledì 26 giugno 13
7. 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
8. 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
9. 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
10. ...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
19. 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
20. 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
21. partial mock
class MyClass
{
! private $value;
! public __construct($value)
! {
! ! $this->value = $value;
! }
! public function foo()
! {
! ! return $this->value;
! }
}
mercoledì 26 giugno 13
22. 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
23. 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
24. stubbing magic methods
class MagicClass
{
public function __call($method, $args)
{
return '__call';
}
}
mercoledì 26 giugno 13
25. 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
26. 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
27. 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
28. 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
29. 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
30. 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
31. 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
32. 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
33. 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
34. verify magic calls
class MagicClass
{
public function __call($method, $args)
{
return '__call';
}
}
mercoledì 26 giugno 13
35. 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
36. 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
37. 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
38. 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
39. 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
40. 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
41. 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