我在理解模拟对象时遇到了一些问题。
我想要的是正常工作的观察者,但确保使用正确的参数调用方法。
据我所知,到目前为止,这应该是我正在寻找的: 观察者:
class Observer
{
public function returnFooIfBar($bar)
{
return ($bar == 'bar') ? 'foo' : ;
}
}
主题:
class Subject
{
$obs;
__construct(Observer $dependency)
{
$this->obs = $dependency;
}
public function tradeStrings($string)
{
$this->obs->returnFooIfBar($string);
}
}
测试:
class SubjectTest
{
public function testCallsObsMethod()
{
$obs = $this->getMock('Observer') ;
$obs->expect($this->once())
->method('returnFooIfBar')
->with($this->equlTo('bar')) ;
$subj = new Subject($obs);
$returnString= $subj->TradeStrings('bar') ;
$this->assertEqual('foo', $returnString) ;
}
}
根据我的理解,这测试了:
据我所知,除了没有运行构造函数/自动加载之外,没有更改原始类的功能。
如果我在运行getMock()时模拟一个方法,那么模拟对象的方法将仅返回一些东西,如果我指定它。
$obs = $this->getMock('Observer', array('returnFooIfBar'));
$obs->expects($this->once())
->method('returnFooIfBar')
->with('bar')
->will($this->returnValue('foo');
我明白这一点吗?如果没有,请你为我澄清一下,因为我希望对此有所了解。 :)
编辑:更改帖子,让我更清楚自己的目标以及我目前的理解方式。
答案 0 :(得分:7)
如果让phpunit创建一个模拟对象,它会在内部构建一个新的临时类,它扩展原始的类,并使用模拟特定代码实现该类的所有方法。
这背后的想法是在测试用例中解耦对象。虽然您的给定示例有效,但您不会以这种方式使用它。
但是你的示例测试无论如何都会失败,因为模拟函数returnStringFooIfBar
不会返回任何内容。
这是它应该如何运作的:
$obs = $this->getMock('observer') ;
$obs->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'))
->will($this->returnValue('foo'));
$returnString= $obs->returnStringFooIfBar ('bar') ;
$this->assertEqual('foo', $returnString) ;
但真实世界的测试案例会涉及一些测试对象:
class TestObject {
private $observer;
public function __construct($observer) {
$this->observer = $observer;
}
public function doMagicAndNotify() {
// do heavy magic
//notify observer
$obsresult = $this->observer->returnStringFooIfBar('bar');
return 'didmagic';
}
}
class TestObjectTest extends PHPUnit_Framework_TestCase {
public function testObserverCalling() {
$obs = $this->getMock('observer') ;
$obs->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'));
$test = new TestObject($obs);
$returnString= $test->doMagicAndNotify() ;
$this->assertEqual('didmagic', $returnString) ;
}
}
修改强>
我想要的是一个正常工作的观察者,但确保使用正确的参数调用这些方法。
据我所知,原始类的功能没有改变, 除了没有运行构造函数/自动加载。
实际上是另一种方式。 Observer的临时子类将覆盖所有(或指定的)方法并更改原始功能(不会执行模拟方法的父级)。它不会覆盖构造函数,无论如何都会调用它。
无法断言模拟方法的方法调用并在同一个sime中调用其原始方法。
请参阅Method Template和the Generator以供参考。
请记住:你没有在这里测试观察者的正确行为,你正在嘲笑它的行为以测试这个主题。
sitenote:$this->returnCallback($someCallback)
是一个强大的功能,可能会帮助你。
我不喜欢这个想法,但你可以这样做:
public function testObserverCalling() {
$obs = new Observer();
$obsmock = $this->getMock('observer') ;
$obsmock->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'))
->will($this->returnCallback(array($obs, 'returnStringFooIfBar')));
$test = new TestObject($obsmock);
$returnString= $test->doMagicAndNotify() ;
$this->assertEqual('didmagic', $returnString) ;
}
答案 1 :(得分:1)
您现在可以使用enableProxyingToOriginalMethods()
在phpunit中执行此操作。
class SubjectTest extends \PHPUnit\Framework\TestCase
{
public function testCallsObsMethod()
{
$obs = $this->getMockBuilder('Observer')
->enableProxyingToOriginalMethods()
->getMock();
$obs->expect($this->once())
->method('returnFooIfBar');
$subj = new Subject($obs);
$returnString= $subj->tradeStrings('bar') ;
$this->assertEqual('foo', $returnString) ;
}
}