Phpspec and his friend prophecy are great tools. They help build better code, by encouraging correct design decisions, and, more importantly, to think about your units.

The problem (if any)

A lot of issues are open talking about Generators, Iterators and Traversables:

Enough to see there is actually some room for improvement. Sure enough ?
Iterators are not that easy to describe, as they can have a lot of different responsibilities and behaviors, not talking about the horrible fact that they maintain a visible state, full of side effects. The api is not clean, the different implementations behave the way they want; enough to avoid them!

One (potential) solution

You have maybe noticed, lately I started playing with php 5.5 generators1.

While I was speccing something using a MongoCursor1, I felt the pain. It took me time and suffering to specify how my MongoCursor1 would behave, when it should have been easy!
It’s just an array2, after all!

Here is how it looks like:

<?php
function its_findBy_retrieves_emitter_specific_events(\MongoCursor $cursor, Event $event)
{
    $cursor->rewind()->willReturn();
    $cursor->count()->willReturn(1);
    $cursor->valid()->willReturn(true, false);
    $cursor->next()->willReturn();
    $cursor->current()->willReturn($event);
    $events = $this->findBy('A\Test\FQCN', 1);
    $events->shouldHaveType('Traversable');
}

Maybe it’s not that complicated. Once you know.
For those who don’t, I came up with a little phpspec extension, which is a totally untested POC!

It greatly simplifies the definition of Iterators behavior. See by yourself with the previous example:

<?php
function its_findBy_retrieves_emitter_specific_events(\MongoCursor $cursor, Event $event)
{
    $cursor->iterates([$event]);
    $events = $this->findBy('A\Test\FQCN', 1);
    $events->shouldHaveType('Traversable');
}

The (crappy) implementation

I haven’t find a better way to do this, so currently I had to extend3 the base Collaborator class to add an iterates method.
This method simply automates the configuration of the Iterator double, mainly via its next, current, valid and rewind methods.

The rest is just usual extension boilerplate, except one particularly interesting part: the Maintainer class.

This one is the one who decides wether or not it should replace the current (standard) collaborator with a more specific one, oriented for Iterators (thanks to its iterates method).
It does that using php reflection capabilites, yielding every collaborator that is actually an Iterator1.

Interested ? Check it and let me know!

Notes

  1. Which is an Iterator :) 2 3 4

  2. In it’s purest form, at least

  3. Beurk