Class Constants

When creating a test double for a class, Mockery does not create stubs out of any class constants defined in the class we are mocking. Sometimes though, the non-existence of these class constants, setup of the test, and the application code itself, it can lead to undesired behavior, and even a PHP error: PHP Fatal error:  Uncaught Error: Undefined class constant 'FOO' in ...`

While supporting class constants in Mockery would be possible, it does require an awful lot of work, for a small number of use cases.

We can, however, deal with these constants in a way supported by Mockery - by using Named Mocks.

A named mock is a test double that has a name of the class we want to mock, but under it is a stubbed out class that mimics the real class with canned responses.

Lets look at the following made up, but not impossible scenario:

class Fetcher
{
    const SUCCESS = 0;
    const FAILURE = 1;

    public static function fetch()
    {
        // Fetcher gets something for us from somewhere...
        return self::SUCCESS;
    }
}

class MyClass
{
    public function doFetching()
    {
        $response = Fetcher::fetch();

        if ($response == Fetcher::SUCCESS) {
            echo "Thanks!" . PHP_EOL;
        } else {
            echo "Try again!" . PHP_EOL;
        }
    }
}

Our MyClass calls a Fetcher that fetches some resource from somewhere - maybe it downloads a file from a remote web service. Our MyClass prints out a response message depending on the response from the Fetcher::fetch() call.

When testing MyClass we don’t really want Fetcher to go and download random stuff from the internet every time we run our test suite. So we mock it out:

// Using alias: because fetch is called statically!
\Mockery::mock('alias:Fetcher')
    ->shouldReceive('fetch')
    ->andReturn(0);

$myClass = new MyClass();
$myClass->doFetching();

If we run this, our test will error out with a nasty PHP Fatal error:  Uncaught Error: Undefined class constant 'SUCCESS' in ...

Here’s how a namedMock() can help us in a situation like this.

We create a stub for the Fetcher class, stubbing out the class constants, and then use namedMock() to create a mock named Fetcher based on our stub:

class FetcherStub
{
    const SUCCESS = 0;
    const FAILURE = 1;
}

\Mockery::mock('Fetcher', 'FetcherStub')
    ->shouldReceive('fetch')
    ->andReturn(0);

$myClass = new MyClass();
$myClass->doFetching();

This works because under the hood, Mockery creates a class called Fetcher that extends FetcherStub.

The same approach will work even if Fetcher::fetch() is not a static dependency:

class Fetcher
{
    const SUCCESS = 0;
    const FAILURE = 1;

    public function fetch()
    {
        // Fetcher gets something for us from somewhere...
        return self::SUCCESS;
    }
}

class MyClass
{
    public function doFetching($fetcher)
    {
        $response = $fetcher->fetch();

        if ($response == Fetcher::SUCCESS) {
            echo "Thanks!" . PHP_EOL;
        } else {
            echo "Try again!" . PHP_EOL;
        }
    }
}

And the test will have something like this:

class FetcherStub
{
    const SUCCESS = 0;
    const FAILURE = 1;
}

$mock = \Mockery::mock('Fetcher', 'FetcherStub')
$mock->shouldReceive('fetch')
    ->andReturn(0);

$myClass = new MyClass();
$myClass->doFetching($mock);