Considering what to test with phpspec

I have recently started using phpspec in place of PHPUnit as a PHP development tool. So far for me, the transition from TDD to specBDD is a fairly trivial one. The vocabulary used in the specs is slightly different to xUnit tools, and this seems to make me think more carefully about what I really want to test, or better, what behaviour I want my object to exhibit.

That’s the key for me; I’m not testing the object, I’m declaring its expected behaviour. BDD, not TDD.

The language that is used in specs is all future tense. You’re not performing an operation and testing the result. Instead, you declare what the object should do. The use of “should” in this way implies that the object doesn’t do it yet, but at some point in the future it will.

It’s not so much a change of approach as a change of mindset. The familiar cycle of red, green, refactor is just the same as it was. What’s different is how your thought process is guided. I find that there is no consideration for testing implementation details, an anti-pattern that xUnit tools make it possible to fall into. All the spec lets you care about is what the object should appear to do to an outside observer. What you get from the object’s interface. It’s that simple.

An example

Here’s an example of something that happened to me yesterday. I have an object that implements the Countable interface, and accepts an array into its constructor. The count() method simply returns the length of the array. To test that this works as expected using PHPUnit, I would possibly have written the following test and moved on:

This test would work. I might have wanted to test other conditions, like an empty array, but fundamentally this does what is required. Under the hood, PHPUnit checks that $obj implements Countable, and then calls the count($obj) and compares the result to my assertion.

But wait…

I tried to recreate this test with phpspec, and found I didn’t know how. So I reached out to the phpspec community at Inviqa to ask whether it was enough to check that the Countable interface was properly implemented, and got the following reply:

the MyObject::count() method is part of your public interface and that’s what you should spec

This made me think. As a PHP developer, I know what the expectations of the built in count() function are. I know that my object must implement Countable, and provide a public count() method that returns something resembling an integer. So why am I tempted to test a feature of the language? That’s something out of my control. What is in my control is that I implement the correct interface and return the correct value from my public interface. With that in mind, I wrote these specs:

What’s the difference, and does it matter?

The difference is that by using the built in count() function in your test you are inadvertently testing the functionality of a language feature. By only testing the requirements set down by that language feature as they apply to your object, you are only testing your object specification.

Whether this matters is something that will probably split opinion. On one hand, why should I be responsible for testing that the language works as it says? On the other, wouldn’t it be beneficial to know when upgrading my PHP version in future, that the built in count() function no longer works as expected?

I’m still in two minds about this. while I like that my tests/specs are more focused, I’m concerned that I’m not doing everything I can to ensure future compatibility with a language feature that I use every day. Either way though, I am very happy with the way that phpspec has made me rethink my testing approach.


  • 8th November 2013 - 6:34 pm | Permalink

    I am not sure I understand your point about testing an inbuilt language feature, calling $this->count() in your spec is not using an inbuilt language feature, it is using the MyObject::count method that you have defined as part of the contract in implementing Countable.

    You are defining behaviour, and therefore you need to write a test – xUnit or PhpSpec.

    Consider a very simple collection

    $collection = new Collection();

    $collection->add('another item');

    // What would you expect here? Probably 3...
    echo count($collection);

    // or
    echo $collection->count();

    This could be simply implemented like this

    items = $items;

    public function add($item)
    $this->items[] = $item;

    public function count()
    return count($this->items);

    But what if you wanted to only return a count of unique items? There a couple of ways this might be achieved, without an example we can never see that behaviour in a broken or validated state.

    add('item 1');
    $this->add('item 2');
    $this->add('item 3');


    // this test would fail
    function it_only_counts_unique_items()


    With that failing test we can pick an implementation (for my example either implementations, or both would work)

    class Collection implements Countable
    // ...

    public function add($item)
    if (!in_array($item, $this->items)) {
    $this->items[] = $item;

    // alternatively use array_unique
    public function count()
    return count(array_unique($this->items));

    • 8th November 2013 - 9:14 pm | Permalink

      I am not sure I understand your point about testing an inbuilt language feature, calling $this->count() in your spec is not using an inbuilt language feature, it is using the MyObject::count method that you have defined as part of the contract in implementing Countable.

      That was the point I was making really. In the PHPUnit example I tested using the built in count() function, but in the phpspec example, because there was no obvious way of doing that, I was forced to only testing the actual implementation of the object’s interface.

      You are also absolutely right. This was only a very simple example, and in a more complex collection you need specs to cover all of the describable behaviours of your object.

  • 8th November 2013 - 6:43 pm | Permalink

    Code formatting failure… here is a gist instead

  • 8th November 2013 - 10:43 pm | Permalink

    So are we talking about the fact that when using PHPunit, if you strip away the implementation detail of the assertion framework, doing

    $this->assertCount(2, $obj);

    basically results in

    if (count($obj) === 2) // ..

    whereas in PhpSpec when you use


    this results in a check similar to

    if ($obj->count() === 2) // ...

    • 9th November 2013 - 11:20 am | Permalink

      Yeah exactly. In the case of phpspec we are guided into testing the object implementation. Of course, in PHPUnit we could be more care about our test:

      $this->assertEquals(2, $obj->count());

      But the point I was making is that phpspec naturally guides us to this approach.

  • 16th July 2014 - 2:58 pm | Permalink

    Perhaps the problem isn’t “testing language features”, but “thinking in terms of language features”.

    Tests should cover the intended usage of the code, as much as is practical. They should verify expected behaviour when the code is used in expected ways; this includes edge-cases, but doesn’t include hacks (uses which work by coincidence due to implementation details) or abuse (uses which bypass the design).

    This is usually called testing the “public API”, but that term unfortunately conflicts with the OO meaning of “methods of an object which are callable from outside that object’s methods”. A lot of the time these will overlap, but treating them as the same thing can cause confusion like in this case.

    Here it looks like “being passed to ‘count'” is an intended use case, so that’s part of your public API. I don’t know whether you expect anyone to call “MyObject::count” directly, but for the sake of argument let’s say you don’t.

    In that case, “MyObject::count” isn’t part of your public API and doesn’t need testing individually. The fact that it’s defined as “public” in the OO sense is an implementation detail, required to make ‘count’ work. As an implementation detail, this method’s existence and behaviour is undefined and anyone relying on it is using a hack.

    Now, let’s change our perspective and ask whether the code and its API are *well designed*. In my hypothetical example, the API is well-designed but the implementation isn’t since implementation details aren’t encapsulated behind well-defined entry-points. This is known as a “leaky abstraction” since, like a leaky roof, it fails in its job to keep our nice clean house isolated from the rest of the world.

    There are several ways we can improve this design:
    * We could try to remove “MyObject::count” or make it private. This plugs the leak, but it would also stop “count” working so it’s not a viable implementation.
    * We could make “MyObject::count” part of the public API. This doesn’t plug the leak, but it’s like putting a bucket underneath and declaring it to be part of the roof: we’re violating Don’t Repeat Yourself and adding warts to our public API.
    * We could remove “being passed to ‘count'” from our public API. This would be a breaking change, but would result in a clean API and leak-proof implementation. Unfortunately it would also be “reinventing the square wheel” since ‘count’ is already the accepted way to count things in PHP.

    I would go with the second option. It’s not really our fault that DRY is broken, it’s PHP’s fault for having ‘count’ be the de facto standard for counting things, whilst requiring implementations to expose a redundant entry-point.

    If we decide to define “MyObject::count” as part of our public API, then we should test it just like everything else.

  • 10th November 2014 - 5:51 pm | Permalink

    I’m not sure if this is intended or what but few years down the line the shouldHaveCount matcher seems to go away from such behaviour as it actually use count against the subject:

    Also I think I might have to give a look to this because the error message given by the tool when trying to assert those type of things it is very misleading.

  • Leave a Reply to Shane Cancel reply

    Your email address will not be published. Required fields are marked *