代码之家  ›  专栏  ›  技术社区  ›  rr.

phpunit“mocked方法不存在。”使用$mock->expected时($this->at(…))

  •  37
  • rr.  · 技术社区  · 14 年前

    我遇到了一个关于phpunit模拟对象的奇怪问题。我有一个方法应该调用两次,所以我使用的是“at”匹配器。这是第一次调用该方法,但出于某种原因,第二次调用该方法时,我得到“mocked method does not exist.”。我以前用过“at”火柴,从来没有碰到过这个。

    我的代码看起来像:

    class MyTest extends PHPUnit_Framework_TestCase
    {
        ...
    
        public function testThis()
        {
            $mock = $this->getMock('MyClass', array('exists', 'another_method', '...'));
            $mock->expects($this->at(0))
                 ->method('exists')
                 ->with($this->equalTo('foo'))
                 ->will($this->returnValue(true));
    
            $mock->expects($this->at(1))
                 ->method('exists')
                 ->with($this->equalTo('bar'))
                 ->will($this->returnValue(false));
        }
    
        ...
    }
    

    当我运行测试时,我得到:

    Expectation failed for method name is equal to <string:exists> when invoked at sequence index 1.
    Mocked method does not exist.
    

    如果我移除第二个匹配器,我不会得到错误。

    以前有人碰到过这个吗?

    谢谢!

    7 回复  |  直到 6 年前
        1
  •  42
  •   rr.    14 年前

    问题最终在于我如何理解“at”媒人的工作方式。另外,我的例子并不是逐字逐句的,它在我的单元测试中是怎样的。我认为“at”matcher计数器是基于每个查询工作的,实际上它是基于每个对象实例工作的。

    例子:

    class MyClass {
    
        public function exists($foo) {
            return false;
        }
    
        public function find($foo) {
            return $foo;
        }
    }
    

    不正确的:

    class MyTest extends PHPUnit_Framework_TestCase
    {
    
        public function testThis()
        {
            $mock = $this->getMock('MyClass');
            $mock->expects($this->at(0))
                 ->method('exists')
                 ->with($this->equalTo('foo'))
                 ->will($this->returnValue(true));
    
            $mock->expects($this->at(0))
                 ->method('find')
                 ->with($this->equalTo('foo'))
                 ->will($this->returnValue('foo'));
    
            $mock->expects($this->at(1))
                 ->method('exists')
                 ->with($this->equalTo('bar'))
                 ->will($this->returnValue(false));
    
            $this->assertTrue($mock->exists("foo"));
            $this->assertEquals('foo', $mock->find('foo'));
            $this->assertFalse($mock->exists("bar"));
        }
    
    }
    

    对的:

    class MyTest extends PHPUnit_Framework_TestCase
    {
    
        public function testThis()
        {
            $mock = $this->getMock('MyClass');
            $mock->expects($this->at(0))
                 ->method('exists')
                 ->with($this->equalTo('foo'))
                 ->will($this->returnValue(true));
    
            $mock->expects($this->at(1))
                 ->method('find')
                 ->with($this->equalTo('foo'))
                 ->will($this->returnValue('foo'));
    
            $mock->expects($this->at(2))
                 ->method('exists')
                 ->with($this->equalTo('bar'))
                 ->will($this->returnValue(false));
    
            $this->assertTrue($mock->exists("foo"));
            $this->assertEquals('foo', $mock->find('foo'));
            $this->assertFalse($mock->exists("bar"));
        }
    
    }
    
        2
  •  16
  •   yvoyer    12 年前

    仅供参考,不确定它是否相关,但我遇到了同样的事情,但不是与 $this->at() 方法,对我来说 $this->never() 方法。

    这引发了错误

    $mock->expects($this->never())
        ->method('exists')
        ->with('arg');
    

    这修正了错误

    $mock->expects($this->never())
        ->method('exists');  
    

    它在使用 $this->exactly(0) 方法。

    希望这能帮助别人。

        3
  •  5
  •   Jon B    12 年前

    尝试改变 $this->at(1) $this->at(2)

        4
  •  3
  •   Tyler Collier    10 年前

    这是phpunit错误消息的不幸措辞。

    仔细检查您的通话顺序,如@rr's answer notes。

    对我来说,就我自己的代码所知,我应该使用 at(0) at(1) 但直到我使用 at(2) at(3) 相反,它起作用了。(我使用cakephp中的会话模拟。)

    检查顺序的最佳方法是“进入”被调用的方法并检查传递的内容。你可以这样做:

    $cakePost = $this->getMock('CakePost');
    $cakePost->expects($this->once())
    ->method('post')
    ->with(
        // Add a line like this for each arg passed
        $this->callback(function($arg) {
            debug("Here's what was passed: $arg");
        })
    );
    
        5
  •  1
  •   edorian    14 年前

    从演示代码中我可以看出 应该 工作。我制作了一个工作示例,以防您运行的是旧的phpunit版本,并希望检查它是否也适用于您。

    如果这没有帮助,也许您可以提供更多(最好是可执行的)代码?:)

    <?php
    
    class MyTest extends PHPUnit_Framework_TestCase
    {
    
        public function testThis()
        {
            $mock = $this->getMock('MyClass');
            $mock->expects($this->at(0))
                 ->method('exists')
                 ->with($this->equalTo('foo'))
                 ->will($this->returnValue(true));
    
            $mock->expects($this->at(1))
                 ->method('exists')
                 ->with($this->equalTo('bar'))
                 ->will($this->returnValue(false));
    
            $this->assertTrue($mock->exists("foo"));
            $this->assertFalse($mock->exists("bar"));
        }
    
    }
    
    class MyClass {
    
        public function exists($foo) {
            return false;
        }
    }
    

    印刷

    phpunit MyTest.php
    PHPUnit 3.4.15 by Sebastian Bergmann.
    
    .
    
    Time: 0 seconds, Memory: 4.25Mb
    
    OK (1 test, 3 assertions)
    
        6
  •  0
  •   martinvium    14 年前

    你确定你的测验中包括了我的课吗?在模拟类/接口而不包括它时,我有一些未定义的方法错误。

        7
  •  0
  •   Mubashar    6 年前

    可能不是今天提出问题的时候 documentation 明确说明应如何使用at和i引用

    注释
    at()matcher的$index参数引用给定模拟对象的所有方法调用中从零开始的索引。使用这个匹配器时要小心,因为它可能导致脆弱的测试,这些测试与具体的实现细节紧密相连。