14
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
.
你正在构造函数中工作。要将对象设置为状态,请获取远程文件。但该步骤不是必需的,因为该对象不需要该数据处于有效状态。在实际调用getFormatted之前,您不需要文件中的结果。
You could defer loading:
你可以推迟加载:
class ClassA {
private $dog;
private $formatted;
public function __construct($param1) {
$this->dog = $param1;
}
protected getResultFromRemoteServer() {
if (!$this->formatted) {
$this->formatted = file_get_contents(
'http://whatever.com/index.php?' . $this->dog
);
}
return $this->formatted;
}
public getFormatted() {
return ("The dog is a " . $this->getResultFromRemoteServer());
}
}
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 stub getFormatted
instead. You also won't need to open your API for the testing and make getResultFromRemoteServer
public then.
所以你懒得加载远程访问它实际需要的时候。现在你根本不需要存根getResultFromRemoteServer,但可以改为存根getFormatted。您也不需要打开API进行测试,然后公开getResultFromRemoteServer。
On a sidenote, even if it's just an example, I would rewrite that class to read
在旁注中,即使它只是一个例子,我也会重写该类来阅读
class DogFinder
{
protected $lookupUri;
protected $cache = array();
public function __construct($lookupUri)
{
$this->lookupUri = $lookupUri;
}
protected function findById($dog)
{
if (!isset($this->cache[$dog])) {
$this->cache[$dog] = file_get_contents(
urlencode($this->lookupUri . $dog)
);
}
return $this->cache[$id];
}
public function getFormatted($dog, $format = 'This is a %s')
{
return sprintf($format, $this->findById($dog));
}
}
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.
由于它是一个Finder,现在实际上有findById可能更有意义。只是保护它,因为这就是你的例子。
The other option would be to extend the Subject-Under-Test and replace the method getResultFromRemoteServer
with your own implementation returning Poodle
. This would mean you are not testing the actual ClassA
, but a subclass of ClassA
, but this is what happens when you use the Mock API anyway.
另一种选择是扩展Subject-Under-Test并将方法getResultFromRemoteServer替换为您自己的实现返回Poodle。这意味着您不是在测试实际的ClassA,而是测试ClassA的子类,但这是在您使用Mock API时会发生的情况。
As of PHP7, you could utilize an Anonymous class like this:
从PHP7开始,您可以使用这样的Anonymous类:
public function testPoodle() {
$stub = new class('dog52') extends ClassA {
public function getResultFromRemoteServer() {
return 'Poodle';
}
};
$expected = 'This dog is a Poodle';
$actual = $stub->getFormatted();
$this->assertEquals($expected, $actual);
}
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.
在PHP7之前,您只需编写一个扩展测试对象的常规类,并使用它来代替测试对象。或者使用disableOriginalConstructor,如本页其他地方所示。