PHPUnit: Mocking __get() приводит к "__get() должен принимать ровно 1 аргумент..."

У меня проблема с издевательством перегруженного метода __get ($ index). Код для класса, который будет издеваться, и тестируемая система, которая его потребляет, выглядит следующим образом:

<?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;
    }
}

Тест выглядит следующим образом:

<?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());
    }
}

Реальный код немного сложнее, хотя и не так много, имея "getSnacks()" в своем родительском классе. Но этого примера должно быть достаточно.

Проблема в том, что при выполнении теста с PHPUnit возникает следующая ошибка:

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

Когда я отлаживаю, я не могу даже дойти до метода тестирования. Кажется, он ломается при настройке макетного объекта.

Любые идеи?

Ответ 1

Вы можете попробовать с помощью returnCallback вместо returnValue:

$this->mock->expects($this->once())
    ->method('__get')
    ->will($this->returnCallback(array($this, 'callbackMethod')));

Затем вызывается метод callbackMethod с параметрами __get.

Ваш метод обратного вызова может выглядеть примерно так:

public function callbackMethod()
{
    return 'snickers';
}

См: http://www.phpunit.de/manual/3.5/en/test-doubles.html#test-doubles.stubs.examples.StubTest5.php

Ответ 2

Посмотрите на насмешливый магический метод __get. Вероятно, вы вызываете там еще один метод __get из другого и не должным образом издеваетесь над объектом.

Ответ 3

__get() принимает аргумент, поэтому вам нужно предоставить mock с одним:

/**
 * @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());
}

Метод with() устанавливает аргумент для метода mocked в PHPUnit. Вы можете найти более подробную информацию в разделе Test Doubles.

Ответ 4

withAnyParameters() может помочь вам, это работает правильно:

$this->mock -> expects($this -> once())  
    -> method('__get') -> withAnyParameters()
    -> will($this -> returnValue('snikers'));