【问题标题】:PHPunit mock only observingPHPunit mock 仅观察
【发布时间】:2012-10-22 06:22:22
【问题描述】:

我在理解模拟对象时遇到了一些问题。

我想要的是一个正常工作的观察者,但要确保使用正确的参数调用方法。

据我所知,这应该是我正在寻找的: 观察者:

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) ;
    }
}

据我了解,此测试表明:

  1. Observer::getFooIfBar 被调用一次。
  2. Observer::getFooIfBar 得到字符串 'bar' 3.该方法按照类中定义的方式工作,并将'foo'作为字符串返回。

据我了解,除了没有运行构造函数/自动加载之外,原始类的功能没有改变。

如果我在运行 getMock() 时模拟一个方法,则模拟对象的方法将在我指定时返回一些内容。

$obs = $this->getMock('Observer', array('returnFooIfBar'));
$obs->expects($this->once())
    ->method('returnFooIfBar')
    ->with('bar')
    ->will($this->returnValue('foo');

我理解对了吗?如果不能,请您为我澄清一下,因为我希望对此有所了解。 :)

编辑:更改了帖子,以更清楚地说明我所追求的以及我目前对它的理解。

【问题讨论】:

    标签: php phpunit


    【解决方案1】:

    如果您让 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 的临时子类覆盖所有(或指定)方法并更改原始功能(模拟方法的父级根本不执行)。它不会覆盖构造函数,无论如何都会调用它。

    不能断言模拟方法的方法调用和同时调用其原始方法。

    请参阅 Method Templatethe 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 - 我排了几乎完全相同的帖子,但你打败了我:-)
    • 我已经对我的帖子进行了更改,以试图澄清我的观点,以及我目前对事情的理解。
    • 谢谢,这帮助我更好地掌握了模拟对象的工作原理。对我来说最重要的信息是,模拟只能按规定工作,不保留任何原始功能。我知道我问的关于包含对主题的测试的问题很糟糕。只是不想每次都指定正确的返回值的“麻烦”,但我想它也一样。让我立即在测试中养成良好的习惯。
    【解决方案2】:

    您现在可以在 phpunit 中使用 enableProxyingToOriginalMethods() 执行此操作。

    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) ;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-04-04
      • 2022-01-13
      • 1970-01-01
      • 2017-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多