PHPUnit: использовать Mock для изменения возвращаемого значения? - PullRequest
0 голосов
/ 23 сентября 2011

Мне бы хотелось иметь PHPUnit Mock, который выполняет метод, как обычно, но затем каким-то образом изменяет возвращаемое значение, прежде чем функция вернется.

Что у меня есть

У меня есть набор производных классов, аналогичных приведенным ниже:

abstract class Base
{
    abstract protected function getUrl();
    public function callUrl() {
        $url = $this->getUrl();
        // some code to call the URL here
    }   
}

class Foo extends Base
{
    protected function getUrl() {
        return "http://www.example.com/Foo";
    }   
}

class Bar extends Base
{
    protected function getUrl() {
        return "http://www.example.com/Bar";
    }   
}

Обратите внимание, что у меня классы намного сложнее, и некоторые из предметов, которые я должен проверить, имеют побочные эффекты (например, запись в базу данных и т. Д.).

Наивный подход с дублированием кода

Если бы у меня был только один производный класс (например, Foo), то я мог бы сделать следующее:

class FooMock extends Foo
{   
    protected function getUrl() {
        return parent::getUrl() . "?sandbox";
    }   
}   

class theTest extends PHPUnit_Framework_TestCase
{   
    public function testIt() {
        $mock = new FooMock();
        // assert something
    }
}

К сожалению, это означает, что мне понадобится определенный класс "Mock" для каждого производного класса, который я хочу протестировать, и все они выполняют одну и ту же функцию.

Предпочтительный подход

Вместо этого я хотел бы иметь возможность сделать что-то вроде следующего:

function callback ($returnValue) {
    return $returnValue . "?sandbox";
}   

class theTest extends PHPUnit_Framework_TestCase
{   
    private $mock;

    public function testFoo() {
        $this->mock = $this->getMockBuilder('Foo')->getMock();
        $this->setupMock();
        // assert something
    }

    public function testBar() {
        $this->mock = $this->getMockBuilder('Bar')->getMock();
        $this->setupMock();
        // assert something
    }

    public function setupMock() {
        $this->mock->expects($this->any())
            ->method('getUrl')
            ->will($this->postProcessReturnValue('callback'));
    }   
}

Это вообще возможно с PHPUnit?


Обновление : Было предложено, чтобы у меня был экземпляр исходного класса и экземпляр ложного класса. Используйте оригинальный класс, чтобы получить исходное возвращаемое значение и изменить его. Это измененное значение затем используется в качестве возврата для макета. Это неосуществимый способ, так как классы более сложные (у них есть побочные эффекты, такие как запись в БД).

Пример, где это не сработает;

class Foo extends Base
{
    $id = 0;
    public function saveToDB() {
        $this->id = saveToDBAndReturnId();
    }
    protected function getUrl() {
        if ($this->id > 0) {
            return "http://www.example.com/".$this->id;
        }
        throw new Exception("No ID");
    }
}
$foo = new Foo();
$foo->saveToDB();
$url = $foo->getUrl();

Очевидно, что возвращаемый URL будет отличаться для нескольких вызовов. Я всегда мог высмеивать saveToDB, но это начинает казаться грязным, когда все, что я хочу сделать, это постобработать результат getUrl.

1 Ответ

5 голосов
/ 23 сентября 2011

PHPUnit позволяет вам определить метод-заглушку, который будет использовать обратный вызов, чтобы определить, что возвращать.

$this->mock->expects($this->any())
     ->method('getUrl')
     ->will($this->returnCallback('callback'));

Вы можете определить свой обратный вызов для вызова исходного метода класса и изменения возвращаемого значения.

Конечно, использование фиктивных объектов таким образом более или менее отрицательно сказывается на том, чтобы они были "фиктивными" объектами, поскольку теперь фиктивные объекты будут полагаться на базовую реализацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...