Skip to content

Instantly share code, notes, and snippets.

@gquemener
Created July 5, 2013 13:26
Show Gist options
  • Save gquemener/5934507 to your computer and use it in GitHub Desktop.
Save gquemener/5934507 to your computer and use it in GitHub Desktop.
Mocking any object methods with mockery
<?php
/**
* @param stdClass $object
*/
function it_should_do_stuff($object)
{
$object->getBar()->willReturn('foo');
$stuffedObject = $this->doStuff($object);
$stuffedObject->getBar()->shouldReturn('FOO');
}
// Like that, no need to create some spec objects
@everzet
Copy link

everzet commented Jul 5, 2013

In order to mock communication, you need to have that communication defined in the first place. There are 2 ways to do that, but both involve explicit definition of objects communication protocol (interface). Here are cases:

Case #1 - "You'll most likely have more than one collaborator of that type".

This case is simple. You need to introduce collaborator types & for that you need communication protocol (aka interface).

<?php

class BarGetterSpec extends ObjecSpec
{
    /**
     * @param BarProviderInterface $object
     */
    function it_should_do_stuff($object)
    {
       $object->getBar()->willReturn('foo');

       $stuffedObject = $this->doStuff($object);
       $stuffedObject->getBar()->shouldReturn('FOO');
    }
}

interface BarProviderInterface
{
    public function getBar();
}

You immediately introduce interface that you'll use for the future objects. You can easily mock those and when you done with your BarGetterSpec, you'll just need to introduce concrete implementation of your BarProviderInterface, which is your explicit communication protocol.

Case #2 - "You'll most likely have only one collaborator of that type".

This is more interesting case, because you'll ask "Why should I introduce clunky interface, when I will have only one implementation of it?". This is a valid concern. So even though you will not need an explicit interface, you still will have an implicit one afterwards, cuz your object will have a public API, which your SUS will use.

So the way you describe such connection is this - you define an explicit communication protocol:

<?php

class BarGetterSpec extends ObjecSpec
{
    /**
     * @param BarProvider $object
     */
    function it_should_do_stuff($object)
    {
       $object->getBar()->willReturn('foo');

       $stuffedObject = $this->doStuff($object);
       $stuffedObject->getBar()->shouldReturn('FOO');
    }
}

interface BarProvider
{
    public function getBar();
}

Notice no Interface suffix. Then, when you done with BarGetterSpec just make your protocol explicit, by replacing it with concrete implementation and start speccing it:

<?php

class BarGetterSpec extends ObjecSpec
{
    /**
     * @param BarProvider $object
     */
    function it_should_do_stuff($object)
    {
       $object->getBar()->willReturn('foo');

       $stuffedObject = $this->doStuff($object);
       $stuffedObject->getBar()->shouldReturn('FOO');
    }
}

class BarProvider
{
    public function getBar()
    {
    }
}

@everzet
Copy link

everzet commented Jul 5, 2013

This also clearly illustrates why both me and @MarcelloDuarte hate this established convention about Interface suffix for interfaces. Don't do that and life would be much easier!

@gquemener
Copy link
Author

Damn, I didn't see your answer 'til today @everzet, thanks for it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment