Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit: Mocking __get() results in "__get() must take exactly 1 argument ..."

I've got a problem with mocking an overloaded __get($index) method. The code for the class to be mocked and the system under test that consumes it is as follows:

<?php
class ToBeMocked
{
    protected $vars = array();

    public function __get($index)
    {
        if (isset($this->vars[$index])) {
            return $this->vars[$index];
        } else {
            return NULL;
        }
    }
}

class SUTclass
{
    protected $mocky;

    public function __construct(ToBeMocked $mocky)
    {
        $this->mocky = $mocky;
    }

    public function getSnack()
    {
        return $this->mocky->snack;
    }
}

Test looks as follows:

<?php    
class GetSnackTest extends PHPUnit_Framework_TestCase
{
    protected $stub;
    protected $sut;

    public function setUp()
    {
       $mock = $this->getMockBuilder('ToBeMocked')
                     ->setMethods(array('__get')
                     ->getMock();

       $sut = new SUTclass($mock);
    }

    /**
     * @test
     */
    public function shouldReturnSnickers()
    {
        $this->mock->expects($this->once())
                   ->method('__get')
                   ->will($this->returnValue('snickers');

        $this->assertEquals('snickers', $this->sut->getSnack());
    }
}

Real code is a little bit more complex, though not much, having "getSnacks()" in its parent class. But this example should suffice.

Problem is I get the following error, when executing the test with PHPUnit:

Fatal error: Method Mock_ToBeMocked_12345672f::__get() must take exactly 1 argument in /usr/share/php/PHPUnit/Framework/MockObject/Generator.php(231)

When I debug I can't even reach the test method. It seems it breaks at setting up the mock object.

Any ideas?

like image 962
beToiba Avatar asked Feb 27 '13 11:02

beToiba


2 Answers

__get() takes an argument, so you need to provide the mock with one:

/**
 * @test
 */
public function shouldReturnSnickers()
{
    $this->mock->expects($this->once())
               ->method('__get')
               ->with($this->equalTo('snack'))
               ->will($this->returnValue('snickers'));

    $this->assertEquals('snickers', $this->sut->getSnack());
}

The with() method sets the argument for the mocked method in PHPUnit. You can find more details in the section on Test Doubles.

like image 100
mAAdhaTTah Avatar answered Sep 28 '22 11:09

mAAdhaTTah


It's a bit hidden in the comments, but @dfmuir's answer put me on the right track. Mocking a __get method is straight forward if you use a callback.

$mock
    ->method('__get')
    ->willReturnCallback(function ($propertyName) {
        switch($propertyName) {
            case 'id':
                return 123123123123;
            case 'name':
                return 'Bob';
            case 'email':
                return '[email protected]';
        }
    }
);

$this->assertEquals('[email protected]', $mock->email);
like image 20
Tom Jowitt Avatar answered Sep 28 '22 10:09

Tom Jowitt