模拟/存根在 PHPUnit 中实现数组访问的类的对象

Posted

技术标签:

【中文标题】模拟/存根在 PHPUnit 中实现数组访问的类的对象【英文标题】:Mocking/Stubbing an Object of a class that implements arrayaccess in PHPUnit 【发布时间】:2012-05-23 08:16:30 【问题描述】:

这是我正在为其编写测试套件的类的构造函数(它扩展了 mysqli):

function __construct(Config $c)

    // store config file
    $this->config = $c;

    // do mysqli constructor
    parent::__construct(
        $this->config['db_host'],
        $this->config['db_user'],
        $this->config['db_pass'],
        $this->config['db_dbname']
    );

传递给构造函数的Config类实现了php内置的arrayaccess接口:

class Config implements arrayaccess...

如何模拟/存根Config 对象?我应该使用哪个?为什么?

提前致谢!

【问题讨论】:

【参考方案1】:

如果您可以轻松地从数组创建 Config 实例,那将是我的首选。虽然您想在可行的情况下单独测试您的单元,但简单的协作者(例如 Config)应该足够安全,可以在测试中使用。设置它的代码可能比等效的模拟对象更容易读写(不易出错)。

$configValues = array(
    'db_host' => '...',
    'db_user' => '...',
    'db_pass' => '...',
    'db_dbname' => '...',
);
$config = new Config($configValues);

话虽如此,您可以像模拟任何其他对象一样模拟实现ArrayAccess 的对象。

$config = $this->getMock('Config', array('offsetGet'));
$config->expects($this->any())
       ->method('offsetGet')
       ->will($this->returnCallback(
           function ($key) use ($configValues) 
               return $configValues[$key];
           
       );

您也可以使用at 来强制执行特定的访问顺序,但这样会使测试变得非常脆弱。

【讨论】:

你甚至可以轻松地做到这一点,但使用数组:-)【参考方案2】:

在提出问题 8 年后,在第一次回答问题 5 年后,我遇到了同样的问题并得出了类似的结论。这就是我所做的,这与大卫接受的答案的第二部分基本相同,只是我使用的是更高版本的 PHPUnit。

基本上你可以模拟ArrayAccess 接口方法。只需要记住,您可能想要同时模拟 offsetGetoffsetExists (在使用它之前,您应该始终检查数组键是否存在,否则您可能会遇到 E_NOTICE 错误和代码中不可预知的行为,如果它没有'不存在)。



$thingyWithArrayAccess = $this->createMock(ThingyWithArrayAccess::class);

$thingyWithArrayAccess->method('offsetGet')
     ->with('your-offset-here')
     ->willReturn('test-value-1');

$thingyWithArrayAccess->method('offsetExists')
     ->with($'your-offset-here')
     ->willReturn(true);

当然,您可以在测试中使用真正的数组,例如


$theArray = [
    'your-offset-here-1' => 'your-mock-value-for-offset-1',
];

$thingyWithArrayAccess = $this->createMock(ThingyWithArrayAccess::class);

$thingyWithArrayAccess->method('offsetGet')
     ->willReturnCallback(
          function ($offset) use ($theArray) 
              return $theArray[$offset];
          
     );

$thingyWithArrayAccess->method('offsetExists')
     ->willReturnCallback(
          function ($offset) use ($theArray) 
              return array_key_exists($offset, $theArray);
          
     );

【讨论】:

哇,过去的大爆炸!自从我在 PHP 中工作以来,已经差不多有这么长的时间了。

以上是关于模拟/存根在 PHPUnit 中实现数组访问的类的对象的主要内容,如果未能解决你的问题,请参考以下文章

在 PHPUnit 中实现给定接口的模拟对象上的未定义方法?

PHPUnit 中的模拟与存根

在 Laravel 5.3 项目中使用 PHPUnit 存根类方法调用的问题

扩展特征的单元测试类 - 我如何在特征中模拟和存根方法?

创建 C++ 类的存根版本

使用C语言为python编写动态模块--在C中实现python中的类