r.bilgil
r.bilgil

Reputation: 909

PHPUnit: Mocking a method that takes in a parameter

I'm creating tests for a class that takes in a "Search" class, which searches a supermarket using a search string and has a method "getItem($itemNo)" that returns the corresponding item.

So, a bit like this:

class MyClass 
{
    public function __construct(Search $search) {
        $item0 = $search->getItem(0);
        $item1 = $search->getItem(1);
        // etc... you get the picture
    }
}

I want to mock this Search class, as I don't want to search the supermarket everytime I do a test.

So I've written:

class MyClassTest extends PHPUnit_Framework_TestCase 
{
    public function setUp()
    {
        $searchResults=$this->getMockBuilder('Search')
                            //Because the constructor takes in a search string:
                            ->disableOriginalConstructor() 
                            ->getMock();

        $pseudoSupermarketItem=array( "SearchResult1", "SearchResult2", etc...);

        $this->searchResult
               ->expects($this->any())
               ->method('getItem')
               ->with(/*WHAT DO I PUT HERE SO THAT THE METHOD WILL TAKE IN A NUMBER*/)
               ->will($this->returnValue($pseudoSupermarketItem[/* THE NUMBER THAT WAS PUT IN */]));
    }
}

As you can see in the code, I would like the mock method to take in an integer, like shown in MyClass, which will then return the corresponding pseudoSupermarketItem string. So far I'm not sure how to make this happen, any help is appreciated!

Upvotes: 5

Views: 12356

Answers (2)

Sven
Sven

Reputation: 70853

PHPUnit offers returnValueMap() for those cases where the input parameters should trigger a defined return value.

// Create a map of arguments to return values.
$map = array(
    array(0, 'SearchResult0'),
    array(1, 'SearchResult1')
);  

// Configure the stub.
$searchResults->expects($this->any())
    ->method('getItem')
    ->will($this->returnValueMap($map));

$this->assertEquals('SearchResult0', $searchResults->getItem(0));
$this->assertEquals('SearchResult1', $searchResults->getItem(1));
$this->assertEquals('SearchResult0', $searchResults->getItem(0));

The map does look a bit strange at first, because there is not direct key->value assignment, but that is because this map also works with more than one input parameter for the mocked method.

$mockedAddition = array(
    array(0, 0, 0),
    array(0, 1, 1),
    array(1, 0, 1),
    array(1, 1, 2),
);

$calculator->expects($this->any())
    ->method('add')
    ->will($this->returnValueMap($mockedAddition);

$this->assertSame(0, $calculator->add(0, 0)); // Returns the value x of (0, 0, x)
$this->assertSame(1, $calculator->add(1, 0)); // Returns the value x of (1, 0, x)
$this->assertSame(1, $calculator->add(0, 1)); // Returns the value x of (0, 1, x)

Upvotes: 2

Cyprian
Cyprian

Reputation: 11364

This should work for you:

$this->searchResult
    ->expects($this->any())
    ->method('getItem')
    ->with($this->isType('integer'))
    ->will($this->returnCallback(function($argument) use ($pseudoSupermarketItem) {
        return $pseudoSupermarketItem[$argument];
    });

In additional maybe you may find it useful (using onConsecutiveCalls):

http://phpunit.de/manual/3.7/en/test-doubles.html#test-doubles.stubs.examples.StubTest7.php

The thrid way is something like that:

$this->searchResult
    ->expects($this->at(0))
    ->method('getItem')
    ->with($this->equalTo(0))
    ->will($this->returnValue($pseudoSupermarketItem[0]);
$this->searchResult
    ->expects($this->at(1))
    ->method('getItem')
    ->with($this->equalTo(1))
    ->will($this->returnValue($pseudoSupermarketItem[1]);
// (...)

Upvotes: 9

Related Questions