Asked  7 Months ago    Answers:  5   Viewed   41 times

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?

 Answers

18

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.

Wednesday, March 31, 2021
 
BradM
answered 7 Months ago
36

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

}
Saturday, May 29, 2021
 
Terry
answered 5 Months ago
65

According to the documentation you linked:

A GET request will be performed by default - but if you pass in any extra parameters in the form of an Object/Map (key/value pairs) then a POST will occur. Extra parameters passed as a string will still use a GET request.

So the simple solution is to convert your object to a string before passing it to the function. Unfortunately, the documentation doesn't specify the format the string should be in, but I would guess it would be the same as if you were generating the GET request manually.

$("#output").load(
    "/server_output.html?year=2009&country=Canada"
);
Sunday, August 1, 2021
 
Dance Party2
answered 3 Months ago
82

There was a Pull Request for this 2 years ago, but the information never been added in the documentation : https://github.com/sebastianbergmann/phpunit-mock-objects/pull/49

You can pass your concrete method in an array in argument 7 of getMockForAbstractClass().

See the code : https://github.com/andreaswolf/phpunit-mock-objects/blob/30ee7452caaa09c46421379861b4128ef7d95e2f/PHPUnit/Framework/MockObject/Generator.php#L225

Thursday, August 12, 2021
 
shin
answered 3 Months ago
47

It's not a separate request happening, so it's going to use the existing request context. You may want to differentiate the action by its name, and use the ChildActionOnly attribute to mark that action as only being available via RenderAction

Monday, August 16, 2021
 
Abdel
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :