PHPUnit:模拟 __get() 导致“__get() 必须恰好采用 1 个参数 ...”
Posted
技术标签:
【中文标题】PHPUnit:模拟 __get() 导致“__get() 必须恰好采用 1 个参数 ...”【英文标题】:PHPUnit: Mocking __get() results in "__get() must take exactly 1 argument ..." 【发布时间】:2013-02-13 04:27:36 【问题描述】:我在模拟重载的 __get($index) 方法时遇到了问题。 要模拟的类和使用它的被测系统的代码如下:
<?php
class ToBeMocked
protected $vars = array();
public function __get($index)
if (isset($this->vars[$index]))
return $this->vars[$index];
else
return NULL;
class SUTclass
protected $mocky;
public function __construct(ToBeMocked $mocky)
$this->mocky = $mocky;
public function getSnack()
return $this->mocky->snack;
测试如下:
<?php
class GetSnackTest extends PHPUnit_Framework_TestCase
protected $stub;
protected $sut;
public function setUp()
$mock = $this->getMockBuilder('ToBeMocked')
->setMethods(array('__get')
->getMock();
$sut = new SUTclass($mock);
/**
* @test
*/
public function shouldReturnSnickers()
$this->mock->expects($this->once())
->method('__get')
->will($this->returnValue('snickers');
$this->assertEquals('snickers', $this->sut->getSnack());
实际代码稍微复杂一点,虽然不多,但在其父类中有“getSnacks()”。但是这个例子就足够了。
问题是我在使用 PHPUnit 执行测试时收到以下错误:
Fatal error: Method Mock_ToBeMocked_12345672f::__get() must take exactly 1 argument in /usr/share/php/PHPUnit/Framework/MockObject/Generator.php(231)
当我调试时,我什至无法达到测试方法。似乎在设置模拟对象时会中断。
有什么想法吗?
【问题讨论】:
好的,我进一步测试了一点。问题似乎真的出在模拟类的构造上。如果我用 ->setMethods(array('blabla')) 创建它,他就会使用测试方法。如果我使用 ->setMethods(array('__get')) 他会中断。 你是对的 - 问题与 PHP 模拟对象如何生成模拟有关 - 下面的答案都没有解决这个问题。我在 PHPUnit 4.0 中遇到了这个问题 - 你使用的是什么版本? 我尝试使用 PHPUnit 版本的不同组合重新创建它,它是从可用的最新版本(撰写本文时为 4.8.2)回到 PHPUnit 3.5 的 Mock Object 依赖项,但无法获得它使用我的环境以这种方式打破 - Ubuntu 12.04.5 LTS 上的 PHP 5.5。除非原始发帖人可以通过记住他们使用的版本组合来扩展这个问题,否则我建议关闭这个问题。 Phiu,3 年后通过谷歌回到这个问题。 :D 我认为早期是 PHP 5.3 和 PHPUnit 3.6.x 或 3.7.x。好久没报错了。现在我使用 PHP 5.5 / PHPUnit 4.8.26 再次获得它。我认为这是我们所有单元测试以某种方式相互交叉输入的问题。因为回到 3.7.38 我得到了不同的错误。我会尽力将问题本地化一些。也许这只是过去美好时代的一些恶臭测试。 :D 【参考方案1】:__get()
需要一个参数,因此您需要为模拟提供一个:
/**
* @test
*/
public function shouldReturnSnickers()
$this->mock->expects($this->once())
->method('__get')
->with($this->equalTo('snack'))
->will($this->returnValue('snickers'));
$this->assertEquals('snickers', $this->sut->getSnack());
with()
方法设置 PHPUnit 中模拟方法的参数。您可以在Test Doubles 部分找到更多详细信息。
【讨论】:
包含with()
调用是我希望解决的问题,但它不适合我(PHPUnit 4.4)。你在给出这个答案之前test了吗?只是想知道您是否只是在重复文档,还是知道这样做可以工作?
我想我当时使用的是 3.7。这(或类似的东西)有效,但我在写这篇文章时找不到我所引用的内容。但是没有,我测试了这段代码。
干杯。我一定是在做一些愚蠢的事情(我有点 PHP 新手)
FWIW,在您发表评论后,我无法让它在 4.4 上运行,所以这可能不适用于新版本,或者我复制了错误的内容(看起来我在至少)。那么谁知道呢。我在我从中提取的项目中停止使用__get
。
我在使用 with() 时也遇到了问题,只是无法正常工作。我能够使用这个$planMock->method('__get')->willReturnCallback(function ($propertyName) // switch $propertyName and return value );
成功地为每个魔术方法返回正确的响应【参考方案2】:
它在 cmets 中有点隐藏,但 @dfmuir 的回答让我走上了正轨。如果您使用回调,则可以直接模拟 __get
方法。
$mock
->method('__get')
->willReturnCallback(function ($propertyName)
switch($propertyName)
case 'id':
return 123123123123;
case 'name':
return 'Bob';
case 'email':
return 'bob@bob.com';
);
$this->assertEquals('bob@bob.com', $mock->email);
【讨论】:
【参考方案3】:查看模拟的魔术方法__get。可能您从另一个未正确模拟的对象中再调用一个 __get 方法。
【讨论】:
【参考方案4】:您在 GetSnackTest
类的 setUp
方法中所做的操作不正确。
如果您希望执行__get
方法的代码(这将是您测试的重点> 我想),您必须更改在setup
方法中调用setMethods
的方式。
Here's the complete explanation,但这里是相关部分:
传递一个包含方法名的数组
你已经确定的方法:
都是存根, 默认全部返回null, 很容易被覆盖
因此,您需要通过传递 null
或传递包含 一些 方法(您真正想要存根的方法)的数组来调用 setMethods
),但 不是- __get
(因为您实际上希望执行该方法的代码)。
在shouldReturnSnickers
方法中,您只想调用$this->assertEquals('snickers', $this->sut->getSnack());
,而不需要前面带有expect
部分的行。
这将确保您的 __get
方法的代码被实际执行和测试。
【讨论】:
【参考方案5】:withAnyParameters() 方法可以帮助你,这是正确的:
$this->mock -> expects($this -> once())
-> method('__get') -> withAnyParameters()
-> will($this -> returnValue('snikers'));
【讨论】:
以上是关于PHPUnit:模拟 __get() 导致“__get() 必须恰好采用 1 个参数 ...”的主要内容,如果未能解决你的问题,请参考以下文章