Testing Effects of Commands With Phake::capture()

First published at Tuesday, 8 March 2016

This blog post has first been published in the Qafoo blog and is duplicated here since I wrote it or participated in writing it.

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: