Testing Effects of Commands With Phake::capture()
First published at Tuesday 8 March 2016
Warning: This blog post is more then 8 years old – read and use with care.
Testing Effects of Commands With Phake::capture()
Today I want to share a simple trick for the excellent Mocking library Phake (I wrote about it before) when testing state on APIs that don't return values.
Testing becomes harder when you are using command / query separation in your code and service operations don't return values anymore. If your command method creates objects and passes them down the stack, then you usually want to make assertions on the nature of the changes.
Take the following example that creates a new Order
object:
<?php
class CheckoutHandler
{
private $orderRepository;
private $productRepository;
public function __construct($orderRepository, $productRepository)
{
$this->orderRepository = $orderRepository;
$this->productRepository = $productRepository;
}
public function checkout(Checkout $command)
{
$order = new Order();
$order->setAddress($command->address);
foreach ($command->productIds as $id => $amount) {
$product = $this->productRepository->find($id);
$order->addItem($product, $amount);
}
$this->orderRepository->save($order);
}
}
A "usual" PHPUnit test for this class can only make a single assertion that the OrderRepository
is called with an Order
object. But we might want know if a product was correctly assigned.
With Phake::capture($value)
we can assign the argument passed to OrderRepository#save($order)
to a variable that is available inside the Unit-Test, ready to run assertions on.
<?php
class CheckoutHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testCheckout()
{
$orderRepository = \Phake::mock(OrderRepository::class);
$productRepository = \Phake::mock(ProductRepository::class);
$product = new Product();
\Phake::when($productRepository)->find(42)->thenReturn($product);
$handler = new CheckoutHandler($orderRepository, $productRepository);
$handler->checkout(new Checkout([
'productIds' => [42 => 1],
'address' => new Address(),
]));
\Phake::verify($orderRepository)->save(\Phake::capture($order));
$this->assertEquals(1, count($order->getProducts()));
$this->assertSame($product, $order->getProducts()[0]);
}
}
See after the \Phake::capture($order)
call, the $order
variable contains the argument that was passed to the OrderRepository from your code.
This argueably reaches into the tested class quite a bit, but when you use Command / Query separation and London-Style TDD the only way to observe behaviour and state is mocking. I still think Phake is the best mocking library for PHP and the capture method is another good argument for it.
Subscribe to updates
There are multiple ways to stay updated with new posts on my blog: