I've run into a strange issue with PHPUnit mock objects. I have a method that should be called twice, so I'm using the "at" matcher. This works for the first time the method is called, but for some reason, the second time it's called, I get "Mocked method does not exist.". I've used the "at" matcher before and have never run into this.
My code looks something like:
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));
}
...
}
When I run the test, I get:
Expectation failed for method name is equal to <string:exists> when invoked at sequence index 1.
Mocked method does not exist.
If I remove the second matcher, I do not get the error.
Has anyone run into this before?
Thanks!
The issue ended up being with how I understood the "at" matcher to work. Also, my example was not verbatim how it is in my unit test. I thought the "at" matcher counter worked on a per query basis, where it really works on a per object instance basis.
Example:
class MyClass {
public function exists($foo) {
return false;
}
public function find($foo) {
return $foo;
}
}
Incorrect:
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"));
}
}
Correct:
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"));
}
}
FYI, Not sure if its related, but I encountered the same thing, but not with the $this->at()
method, for me it was the $this->never()
method.
This raised the error
$mock->expects($this->never())
->method('exists')
->with('arg');
This fixed the error
$mock->expects($this->never())
->method('exists');
It did the same thing when using the $this->exactly(0)
method.
Hope this help someone.
Try changing $this->at(1)
to $this->at(2)
This is an unfortunate wording of the error message by PHPUnit.
Double check the order of your calls, like @rr's answer mentions.
For me, as far as I know with my own code, I should be using at(0)
and at(1)
respectively, but it wasn't until I used at(2)
and at(3)
instead that it worked. (I'm using session mocking in CakePHP.)
The best way to check the order is to get 'into' the called method and check what's passed. You can do that like this:
$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");
})
);
As far as i can tell from the Demo code it should work. I produced a working example in case you are running an older PHPUnit Version and want to check that way if it works for you too.
In case that doesn't help maybe you could provide a bit more (at best executable) code ? :)
<?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;
}
}
printing
phpunit MyTest.php
PHPUnit 3.4.15 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 4.25Mb
OK (1 test, 3 assertions)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With