views:

2055

answers:

5

I've got a PHPUnit mock object that returns "return value" no matter what its arguments:

// From inside a test...
$mock = $this->getMock('myObject', 'methodToMock');
$mock->expects($this->any))
     ->method('methodToMock')
     ->will($this->returnValue('return value'));

What I want to be able to do is return a different value based on the arguments passed to the mock method. I've tried something like:

$mock = $this->getMock('myObject', 'methodToMock');
// methodToMock('one')
$mock->expects($this->any))
     ->method('methodToMock')
     ->with($this->equalTo('one'))
     ->will($this->returnValue('method called with argument "one"'));
// methodToMock('two')
$mock->expects($this->any))
     ->method('methodToMock')
     ->with($this->equalTo('two'))
     ->will($this->returnValue('method called with argument "two"'));

But this causes PHPUnit to complain if the mock isn't called with the argument "two", so I assume that the definition of methodToMock('two') overwrites the definition of the first.

So my question is: Is there any way to get a PHPUnit mock object to return a different value based on its arguments? And if so, how?

Thanks in advance!

A: 

Do you mean something like this?

public function TestSomeCondition($condition){
  $mockObj = $this->getMockObject();
  $mockObj->setReturnValue('yourMethod',$condition);
}
Dr. Hfuhruhurr
I think that's SimpleTest code, not PHPUnit. But no, it isn't what I want to achieve.Say I had a mock object that returned a word for a given number. My mock method would need to return "one" when called with 1, "two" when called with 2 etc.$
Ben Dowling
A: 

I had a similar problem which I couldn't work out as well (there's surprisingly little information about for PHPUnit). In my case, I just made each test separate test - known input and known output. I realised that I didn't need to make a jack-of-all-trades mock object, I only needed a specific one for a specific test, and thus I separated the tests out and can test individual aspects of my code as a separate unit. I'm not sure if this might be applicable to you or not, but that's down to what you need to test.

JamShady
Unfortunately that wouldn't work in my situation. The mock is being passed into a method I'm testing, and the test method calls the mocked method with different arguments. It's interesting to know that you couldn't solve the problem though. It sounds like this could be a PHPUnit limitation.
Ben Dowling
+8  A: 

Use a callback. e.g. (straight from PHPUnit documentation):

<?php
class StubTest extends PHPUnit_Framework_TestCase
{
    public function testReturnCallbackStub()
    {
        $stub = $this->getMock(
          'SomeClass', array('doSomething')
        );

        $stub->expects($this->any())
             ->method('doSomething')
             ->will($this->returnCallback('callback'));

        // $stub->doSomething() returns callback(...)
    }
}

function callback() {
    $args = func_get_args();
    // ...
}
?>

Do whatever processing you want in the callback() and return the result based on your $args as appropriate.

Howard Sandford
Can you provide a link to the documentation? I can't seem to find it with "the Google"
Kris Erickson
A: 

Try :

->with($this->equalTo('one'),$this->equalTo('two))->will($this->returnValue('return value'));
Rob Bolton
+2  A: 

I had a similar problem (although slightly different... I didn't need different return value based on arguments, but had to test to ensure 2 sets of arguments were being passed to the same function). I stumbled upon using something like this:

$mock = $this->getMock(); $mock->expects($this->at(0))->method('foo')->with(...)->will($this->returnValue(...)); $mock->expects($this->at(1))->method('foo')->with(...)->will($this->returnValue(...));

It's not perfect, since it requires that the order of the 2 calls to foo() is known, but in practice this probably isn't too bad.

Adam