PHPUnit:使用Mock修改返回值?

Posted

技术标签:

【中文标题】PHPUnit:使用Mock修改返回值?【英文标题】:PHPUnit: Use Mock to modify return value? 【发布时间】:2011-11-23 07:25:36 【问题描述】:

我想要一个 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 能做到吗?


更新:有人建议我有一个原始类的实例和一个模拟类的实例。使用原始类获取原始返回值并对其进行修改。然后将此修改后的值用作 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 的结果进行后处理时,我开始觉得很脏。

【问题讨论】:

【参考方案1】:

PHPUnit 允许您定义一个存根方法,该方法将使用回调来确定要返回的内容。

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

你可以定义你的回调来调用原来的类方法并修改返回值。

当然,以这种方式使用模拟对象或多或少违背了让它们成为“模拟”对象的目的,因为模拟对象现在将依赖于底层实现。

【讨论】:

我读过returnCallback,但是我怎么让它调用原始类的方法呢?我唯一的对象是模拟,它会再次调用回调...... 重新;你的第二点。我同意,但在这种情况下,我认为它违反规则而不是打破规则(也许是必要的邪恶)。 您必须让您的代码实例化原始类的对象(您也可以将其存储为测试类的属性),然后您的回调可以只调用原始类的方法原始对象。 这感觉比创建class FooMockclass BarMock 等还要脏,因为它需要更改应用程序代码以适应测试。 好的,但我如何调用原始方法? PHPUnit 是否为此提供了任何便利,还是我不得不做可怕的反射五线?

以上是关于PHPUnit:使用Mock修改返回值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 Laravel 中使用 PHPUnit 的函数“link_to”没有返回值?

用 phpunit 模拟 symfony 安全,返回空值而不是布尔值(不使用预言)

PHPUnit 如何获得模拟方法的真实返回值?

如何使用PHPUnit测试一个方法调用了同一个类的其他方法,但是没有返回值

Spring Boot Test Mockito Mock Void Return,无返回值的方法

Jest.fn-使用jest.mock时返回的值返回未定义