存根类调用的方法构造函数
如何存根 PHPUnit 中被测试类的构造函数调用的方法?例如,下面的简单代码将不起作用,因为当我声明存根方法时,存根对象已经创建,并且我的方法被调用,未存根。
测试类:
class ClassA {
private $dog;
private $formatted;
public function __construct($param1) {
$this->dog = $param1;
$this->getResultFromRemoteServer();
}
// Would normally be private, made public for stubbing
public getResultFromRemoteServer() {
$this->formatted = file_get_contents('http://whatever.com/index.php?'.$this->dog);
}
public getFormatted() {
return ("The dog is a ".$this->formatted);
}
}
测试代码:
class ClassATest extends PHPUnit_Framework_TestCase {
public function testPoodle() {
$stub = $this->getMockBuilder('ClassA')
->setMethods(array('getResultFromRemoteServer'))
->setConstructorArgs(array('dog52'))
->getMock();
$stub->expects($this->any())
->method('getResultFromRemoteServer')
->will($this->returnValue('Poodle'));
$expected = 'This dog is a Poodle';
$actual = $stub->getFormatted();
$this->assertEquals($expected, $actual);
}
}
How does one stub a method in PHPUnit that is called by the class under test's constructor? The simple code below for example won't work because by the time I declare the stubbed method, the stub object has already been created and my method called, unstubbed.
Class to test:
class ClassA {
private $dog;
private $formatted;
public function __construct($param1) {
$this->dog = $param1;
$this->getResultFromRemoteServer();
}
// Would normally be private, made public for stubbing
public getResultFromRemoteServer() {
$this->formatted = file_get_contents('http://whatever.com/index.php?'.$this->dog);
}
public getFormatted() {
return ("The dog is a ".$this->formatted);
}
}
Test code:
class ClassATest extends PHPUnit_Framework_TestCase {
public function testPoodle() {
$stub = $this->getMockBuilder('ClassA')
->setMethods(array('getResultFromRemoteServer'))
->setConstructorArgs(array('dog52'))
->getMock();
$stub->expects($this->any())
->method('getResultFromRemoteServer')
->will($this->returnValue('Poodle'));
$expected = 'This dog is a Poodle';
$actual = $stub->getFormatted();
$this->assertEquals($expected, $actual);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用
disableOriginalConstructor()
以便getMock()
不会调用构造函数。这个名称有点误导,因为调用该方法最终会为$callOriginalConstructor
传递false
。这允许您在手动调用构造函数之前对返回的模拟设置期望。Use
disableOriginalConstructor()
so thatgetMock()
won't call the constructor. The name is a bit misleading because calling that method ends up passingfalse
for$callOriginalConstructor
. This allows you to set expectations on the returned mock before calling the constructor manually.问题不在于方法的存根,而在于你的类。
您正在构造函数中进行工作。为了将对象设置为状态,您需要获取远程文件。但该步骤不是必需的,因为对象不需要该数据处于有效状态。在实际调用 getFormatted 之前,您不需要文件的结果。
您可以推迟加载:
这样您就可以在实际需要时延迟加载远程访问。现在您根本不需要存根
getResultFromRemoteServer
,而是可以存根getFormatted
。您也不需要打开 API 进行测试,然后将getResultFromRemoteServer
公开。顺便说一句,即使它只是一个例子,我也会重写该类以读取
由于它是一个 Finder,所以现在实际上公开
findById
可能更有意义。只是对其进行保护,因为这就是您的示例中的内容。另一个选项是扩展Subject-Under-Test,并将方法
getResultFromRemoteServer
替换为您自己的返回Poodle
的实现代码>.这意味着您没有测试实际的ClassA
,而是测试ClassA
的子类,但这就是您使用 Mock API 时发生的情况。从 PHP7 开始,您可以使用这样的匿名类:
在 PHP7 之前,您只需编写一个扩展 Subject-Under-Test 的常规类,并使用它来代替 Subject-Under -测试。或者使用
disableOriginalConstructor
,如本页其他地方所示。The problem is not the stubbing of the method, but your class.
You are doing work in the constructor. In order to set the object into state, you fetch a remote file. But that step is not necessary, because the object doesn't need that data to be in a valid state. You dont need the result from the file before you actually call
getFormatted
.You could defer loading:
so you are lazy loading the remote access to when it's actually needed. Now you dont need to stub
getResultFromRemoteServer
at all, but can stubgetFormatted
instead. You also won't need to open your API for the testing and makegetResultFromRemoteServer
public then.On a sidenote, even if it's just an example, I would rewrite that class to read
Since it's a Finder, it might make more sense to actually have
findById
public now. Just keeping it protected because that's what you had in your example.The other option would be to extend the Subject-Under-Test and replace the method
getResultFromRemoteServer
with your own implementation returningPoodle
. This would mean you are not testing the actualClassA
, but a subclass ofClassA
, but this is what happens when you use the Mock API anyway.As of PHP7, you could utilize an Anonymous class like this:
Before PHP7, you'd just write a regular class extending the Subject-Under-Test and use that instead of the Subject-Under-Test. Or use
disableOriginalConstructor
as shown elsewhere on this page.