Mocking / Stubbing在PHPUnit中实现arrayaccess的类的Object

时间:2012-05-15 19:03:14

标签: php phpunit arrayaccess

这是我正在编写测试套件的类的构造函数(它扩展了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对象?我应该使用哪个以及为什么?

提前致谢!

2 个答案:

答案 0 :(得分:15)

如果您可以从数组中轻松创建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强制执行特定的访问顺序,但这样做会使测试变得非常脆弱。

答案 1 :(得分:1)

问了问题8年后,即第一次回答5年后,我遇到了同样的问题,并得出了类似的结论。这就是我所做的,基本上与David接受的答案的第二部分相同,只是我使用的是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);
          }
     );