Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPunit: How to mock a method that has a parameter AND a returned value

Using PHPUnit, I wonder if we can mock an object to test if a method is called with an expected parameter, and a returned value?

In the doc, there are examples with passing parameter, or returned value, but not both...

I tried using this:

// My object to test
$hoard = new Hoard();
// Mock objects used as parameters
$item = $this->getMock('Item');
$user = $this->getMock('User', array('removeItem'));
...
$user->expects($this->once())
     ->method('removeItem')
     ->with($this->equalTo($item));
$this->assertTrue($hoard->removeItemFromUser($item, $user));

My assertion fails because Hoard::removeItemFromUser() should return the returned value of User::removeItem(), which is true.

$user->expects($this->once())
     ->method('removeItem')
     ->with($this->equalTo($item), $this->returnValue(true));
$this->assertTrue($hoard->removeItemFromUser($item, $user));

Also fails with the following message: "Parameter count for invocation User::removeItem(Mock_Item_767aa2db Object (...)) is too low."

$user->expects($this->once())
     ->method('removeItem')
     ->with($this->equalTo($item))
     ->with($this->returnValue(true));
$this->assertTrue($hoard->removeItemFromUser($item, $user));

Also fails with the following message: "PHPUnit_Framework_Exception: Parameter matcher is already defined, cannot redefine"

What should I do to test this method correctly.

like image 230
yvoyer Avatar asked Mar 02 '12 01:03

yvoyer


2 Answers

You need to use will instead of with for returnValue and friends.

$user->expects($this->once())
     ->method('removeItem')
     ->with($item)  // equalTo() is the default; save some keystrokes
     ->will($this->returnValue(true));    // <-- will instead of with
$this->assertTrue($hoard->removeItemFromUser($item, $user));
like image 154
David Harkness Avatar answered Oct 08 '22 18:10

David Harkness


I know that it's an old post, but it's on the top in searching for PHPUnit warning Method name matcher is already defined, cannot redefine, so I'll answer too.

There is other reason for such warning message. If you describe a mock behaviour in chaining method like this:

$research = $this->createMock(Research::class);
$research->expects($this->any())
    ->method('getId')
    ->willReturn(1)
    ->method('getAgent')
    ->willReturn(1);

You will get warning Method name matcher is already defined, cannot redefine. Just split it in separate statements and the warning will go away (tested on PHPUnit 7.5 and 8.3).

$research = $this->createMock(Research::class);

$research->expects($this->any())
    ->method('getId')
    ->willReturn(1);

$research->expects($this->any())
    ->method('getAgent')
    ->willReturn(1);
like image 6
Vasiliy Toporov Avatar answered Oct 08 '22 19:10

Vasiliy Toporov