PHPUnit:是否使用Mock修改返回值?

PHPUnit:是否使用Mock修改返回值?,php,unit-testing,phpunit,Php,Unit Testing,Phpunit,我希望有一个PHPUnit Mock,它执行一个类似normal的方法,但是在函数返回之前以某种方式修改返回值 我所拥有的 我有一组派生类,如下所示: 抽象类基类 { 抽象保护函数getUrl(); 公共职能{ $url=$this->getUrl(); //这里有一些调用URL的代码 } } 类Foo扩展了基 { 受保护的函数getUrl(){ 返回“http://www.example.com/Foo"; } } 类栏扩展了基 { 受保护的函数getUrl(){ 返回“http:

我希望有一个PHPUnit Mock,它执行一个类似normal的方法,但是在函数返回之前以某种方式修改返回值

我所拥有的 我有一组派生类,如下所示:

抽象类基类
{
抽象保护函数getUrl();
公共职能{
$url=$this->getUrl();
//这里有一些调用URL的代码
}   
}
类Foo扩展了基
{
受保护的函数getUrl(){
返回“http://www.example.com/Foo";
}   
}
类栏扩展了基
{
受保护的函数getUrl(){
返回“http://www.example.com/Bar";
}   
}
请注意,我拥有的类要复杂得多,而且我必须测试的一些项目有副作用(例如写入数据库等)

天真、重复的代码方法 如果我只有一个派生类(例如,Foo),那么我可以执行以下操作:

FooMock类扩展了Foo
{   
受保护的函数getUrl(){
返回父::getUrl().“?沙盒”;
}   
}   
类测试扩展了PHPUnit\u框架\u测试用例
{   
公共功能测试(){
$mock=newfoomock();
//断言某事
}
}
不幸的是,这意味着对于我想要测试的每个派生类,我需要一个特定的“Mock”类,所有派生类都执行完全相同的函数

首选方法 相反,我希望能够做到以下几点:

函数回调($returnValue){
return$returnValue.“沙盒”;
}   
类测试扩展了PHPUnit\u框架\u测试用例
{   
私用$mock;
公共函数testFoo(){
$this->mock=$this->getMockBuilder('Foo')->getMock();
$this->setupMock();
//断言某事
}
公共函数testBar(){
$this->mock=$this->getMockBuilder('Bar')->getMock();
$this->setupMock();
//断言某事
}
公共函数setupMock(){
$this->mock->expected($this->any())
->方法('getUrl')
->will($this->postprocesseReturnValue('callback');
}   
}
这在PHPUnit中是可能的吗


更新:有人建议我有一个原始类的实例和一个mock类的实例。使用原始类获取原始返回值并对其进行修改。然后将此修改后的值用作模拟的返回值。这不是一种可行的方法,因为类更复杂(它们有副作用,比如写入数据库)

一个这样做行不通的例子

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
的结果进行后期处理时,这就开始让我感觉不舒服了。PHPUnit允许您定义一个存根方法,该方法将使用回调来确定返回什么

$this->mock->expects($this->any())
     ->method('getUrl')
     ->will($this->returnCallback('callback'));
您可以定义回调来调用原始类方法并修改返回值


当然,以这种方式使用mock对象或多或少会破坏将它们作为“mock”对象的目的,因为mock对象现在将依赖于底层实现。

PHPUnit允许您定义一个存根方法,该方法将使用回调来确定返回什么

$this->mock->expects($this->any())
     ->method('getUrl')
     ->will($this->returnCallback('callback'));
您可以定义回调来调用原始类方法并修改返回值


当然,以这种方式使用mock对象或多或少会破坏将它们作为“mock”对象的目的,因为mock对象现在将依赖于底层实现。

我读过关于
returnCallback
,但我如何让它调用原始类的方法?我唯一拥有的对象是mock,它会再次调用回调…Re;你的第二点。我同意,但在这种情况下,我认为它是弯曲规则而不是破坏它们(可能是必要的邪恶)。您必须让代码实例化原始类的对象(也可以将其存储为测试类的属性),然后你的回调就可以调用原始对象上的原始方法。这感觉比仅仅创建
类FooMock
类BarMock
等更脏,因为它需要修改应用程序代码以适应测试。抱歉-我完全没有读到你的评论。这是可以接受的,除了我的类比给出的示例更复杂——还有其他逻辑(在别处自己进行单元测试),我必须在Mock和非Mock对象上执行。似乎太多了。我已经读过关于
returnCallback
,但是我如何让它调用原始类的方法呢?我唯一拥有的对象是mock,它会再次调用回调…Re;你的第二点。我同意,但在这种情况下,我认为它是弯曲规则而不是破坏它们(可能是必要的邪恶)。您必须让代码实例化原始类的对象(也可以将其存储为测试类的属性),然后你的回调就可以调用原始对象上的原始方法。这感觉比仅仅创建
类FooMock
类BarMock
等更脏,因为它需要修改应用程序代码以适应测试。抱歉-我完全没有读到你的评论。这是可以接受的,除了我的类比给出的示例更复杂——还有其他逻辑(在别处自己进行单元测试),我必须在Mock和非Mock对象上执行。似乎太过分了。