Skip to content

Instantly share code, notes, and snippets.

@glenjamin
Last active December 24, 2015 03:59
Show Gist options
  • Save glenjamin/6741033 to your computer and use it in GitHub Desktop.
Save glenjamin/6741033 to your computer and use it in GitHub Desktop.
Shared behaviours in PHPUnit with Traits
<?php
namespace Example;
interface Iterable {
/**
* @return Iterator iterator over all items
*/
public function items();
/**
* @return int number of items in collection
*/
public function length();
}
<?php
namespace Example\Shared;
abstract class AbstractIterable
{
private $data = [];
private $sort = [];
public function __construct(array $data, array $sort)
{
$this->data = $data;
$this->sort = $sort;
}
public function items()
{
foreach ($this->sort as $key) {
yield $this->createItemInstance($this->data[$key]);
}
}
public function length()
{
return count($this->data);
}
abstract protected function createItemInstance(array $data);
}
<?php
namespace Example\Shared;
require_once __DIR__ . "/Shared_AbstractIterable.php";
class AddressList extends AbstractIterable
{
protected function createItemInstance(array $data) {
return new Address($data['latitude'], $data['longitude']);
}
public function northernHemisphere()
{
foreach ($this->items() as $address) {
if ($address->isNorthernHemisphere()) {
yield $address;
}
}
}
}
class Address
{
private $lat;
private $lng;
public function __construct($lat, $lng) {
$this->lat = $lat;
$this->lng = $lng;
}
public function getLatitude()
{
return $this->lat;
}
public function getLongitude()
{
return $this->lng;
}
public function isNorthernHemisphere()
{
return $this->lat < 45;
}
}
<?php
namespace Example\Shared;
require_once __DIR__ . '/Shared_BehaviourOfIterable.php';
require __DIR__ . '/Shared_AddressList.php';
class AddressListTest extends \PHPUnit_Framework_TestCase
{
use BehaviourOfIterable;
public function test_northern_hemisphere_iterator_filters_and_orders()
{
$collection = $this->createIterable(
[
1 => $this->createIterableItem(15, 60),
2 => $this->createIterableItem(55, 60),
3 => $this->createIterableItem(65, 60),
4 => $this->createIterableItem(3, 60),
],
[4, 3, 1, 2]
);
$list = iterator_to_array($collection->northernHemisphere());
$this->assertCount(2, $list);
$this->assertEquals(3, $list[0]->getLatitude());
$this->assertEquals(15, $list[1]->getLatitude());
}
protected function createIterable(array $data, array $order)
{
return new AddressList($data, $order);
}
protected function createIterableItem($latitude = 0, $longitude = 0)
{
return [
'latitude' => $latitude,
'longitude' => $longitude,
];
}
protected function getIterableItemClassName()
{
return 'Example\\Shared\\Address';
}
protected function getIterableItemIdentifier(Address $item)
{
return $item->getLatitude();
}
}
<?php
namespace Example\Shared;
require_once __DIR__ . "/Shared_AbstractIterable.php";
class AppleList extends AbstractIterable
{
protected function createItemInstance(array $data) {
return new Apple($data);
}
}
class Apple
{
private $id;
public function __construct(array $data) {
$this->id = $data['id'];
}
public function getId()
{
return $this->id;
}
}
<?php
namespace Example\Shared;
require_once __DIR__ . '/Shared_BehaviourOfIterable.php';
require __DIR__ . '/Shared_AppleList.php';
class AppleListTest extends \PHPUnit_Framework_TestCase
{
use BehaviourOfIterable;
protected function createIterable(array $data, array $order)
{
return new AppleList($data, $order);
}
protected function createIterableItem($id = 0)
{
return ['id' => $id];
}
protected function getIterableItemClassName()
{
return 'Example\\Shared\\Apple';
}
protected function getIterableItemIdentifier(Apple $item)
{
return $item->getId();
}
}
<?php
namespace Example\Shared;
trait BehaviourOfIterable
{
/**
* @return Iterable instance of the iterable with the provided data and order
*/
abstract protected function createIterable(array $data, array $order);
/**
* @param int $id a unique identifier for the item
* @return array valid item array for use in $data
*/
abstract protected function createIterableItem($id = 0);
/**
* Extract the unique identifier from an item instance
* This will be used to compare to items created via createIterableItem
*
* @return int
*/
abstract protected function getIterableItemIdentifier($item);
/**
* @return string class name of item instances
*/
abstract protected function getIterableItemClassName();
public function test_empty_collection_has_zero_length()
{
$collection = $this->createIterable([], []);
$this->assertEquals(0, $collection->length());
}
public function test_length_reports_collection_length()
{
$collection = $this->createIterable(
[
$this->createIterableItem(),
$this->createIterableItem(),
$this->createIterableItem(),
],
[0, 1, 2]
);
$this->assertEquals(3, $collection->length());
}
public function test_empty_collection_items_doesnt_iterate()
{
$collection = $this->createIterable([], []);
foreach ($collection->items() as $item) {
$this->fail('Did not expect items() to iterate');
}
}
public function test_collection_items_have_correct_type()
{
$collection = $this->createIterable(
[
$this->createIterableItem(),
$this->createIterableItem(),
$this->createIterableItem(),
],
[0, 1, 2]
);
foreach ($collection->items() as $item) {
$this->assertInstanceOf($this->getIterableItemClassName(), $item);
}
}
public function test_collection_items_iterates_in_order()
{
$collection = $this->createIterable(
[
1 => $this->createIterableItem(1),
2 => $this->createIterableItem(2),
3 => $this->createIterableItem(3),
],
$order = [2, 3, 1]
);
$i = 0;
foreach ($collection->items() as $item) {
$this->assertEquals(
$order[$i++],
$this->getIterableItemIdentifier($item)
);
}
}
}
<?php
namespace Example\Single;
class AppleList
{
private $data = [];
private $sort = [];
public function __construct(array $data, array $sort)
{
$this->data = $data;
$this->sort = $sort;
}
public function items()
{
foreach ($this->sort as $key) {
yield new Apple($this->data[$key]);
}
}
public function length()
{
return count($this->data);
}
}
class Apple
{
private $id;
public function __construct(array $data) {
$this->id = $data['id'];
}
public function getId()
{
return $this->id;
}
}
<?php
namespace Example\Single;
require __DIR__ . '/Single_AppleList.php';
class AppleListTest extends \PHPUnit_Framework_TestCase
{
public function test_empty_collection_has_zero_length()
{
$collection = $this->createAppleList();
$this->assertEquals(0, $collection->length());
}
public function test_length_reports_collection_length()
{
$collection = $this->createAppleList(
[
$this->createAppleListItem(),
$this->createAppleListItem(),
$this->createAppleListItem(),
]
);
$this->assertEquals(3, $collection->length());
}
public function test_empty_collection_items_doesnt_iterate()
{
$collection = $this->createAppleList();
foreach ($collection->items() as $item) {
$this->fail('Did not expect items() to iterate');
}
}
public function test_collection_items_have_correct_type()
{
$collection = $this->createAppleList(
[
$this->createAppleListItem(),
$this->createAppleListItem(),
$this->createAppleListItem(),
]
);
foreach ($collection->items() as $item) {
$this->assertInstanceOf('Example\\Single\\Apple', $item);
}
}
public function test_collection_items_iterates_in_order()
{
$collection = $this->createAppleList(
[
1 => $this->createAppleListItem(1),
2 => $this->createAppleListItem(2),
3 => $this->createAppleListItem(3),
],
$order = [2, 3, 1]
);
$i = 0;
foreach ($collection->items() as $item) {
$this->assertEquals($order[$i++], $item->getId());
}
}
protected function createAppleList(array $data=[ ], array $order=[ ])
{
if (empty($order)) {
$order = array_keys($data);
}
return new AppleList($data, $order);
}
protected function createAppleListItem($id = 0)
{
return ['id' => $id];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment