PHPUnit:使用Mock修改返回值?

Jes*_*ord 0 php phpunit unit-testing

我想有一个PHPUnit Mock,它执行一个像normal一样的方法,但是在函数返回之前以某种方式修改返回值.

是)我有的

我有一组派生类,类似于下面:

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";
    }   
}
Run Code Online (Sandbox Code Playgroud)

请注意我的课程要复杂得多,我必须测试的一些项目有副作用(例如写入数据库等).

天真的,重复的代码方法

如果我只有一个派生类(例如; 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
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这意味着我需要为我想要测试的每个派生类创建一个特定的"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'));
    }   
}
Run Code Online (Sandbox Code Playgroud)

PHPUnit可以实现这一点吗?


更新:有人建议我有一个原始类的实例,以及一个mock类的实例.使用原始类获取原始返回值并进行修改.然后将此修改后的值用作模拟的返回值.由于类更复杂(它们具有诸如写入DB的副作用),因此这不是一种可行的方法.

一个不起作用的例子;

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();
Run Code Online (Sandbox Code Playgroud)

显然,返回的URL在多个调用之间会有所不同.我总是可以嘲笑saveToDB,但是当我想要做的就是对后果进行后期处理时,我开始觉得很脏getUrl.

Joh*_*ess 5

PHPUnit允许您定义将使用回调来确定返回内容的存根方法.

$this->mock->expects($this->any())
     ->method('getUrl')
     ->will($this->returnCallback('callback'));
Run Code Online (Sandbox Code Playgroud)

您可以定义回调以调用原始类方法并修改返回值.

当然,以这种方式使用模拟对象或多或少会破坏使它们成为"模拟"对象的目的,因为模拟对象现在将依赖于底层实现.