Kamrul Khan
Kamrul Khan

Reputation: 3350

PHPUnit mock return value and number of times called with specific argument for different methods same class

My scenario is similar to below:

class ToMock
{
    public function iReturn()
    {
        return time();
    }

    public function callMe1($arg1, $arg2)
    {
        return true;
    }

    public function callMe2($arg1, $arg2)
    {
        return true;
    }
}

class ToTest
{
    public function feedMe(ToMock $toMock)
    {
        if ($toMock->iReturn() > 100) {
            $toMock->callMe1(5, 10);
        }

        if ($toMock->iReturn() < 200) {
            $toMock->callMe2(15, 20);
        }
    }
}

Now, I want to test if ToMock::iReturn() returns 150, ToTest::feedMe() calls ToMock::callMe1() once with 5 and 10, and ToMock::callMe2() once with 15 and 20.

I know I can mock the ToMock::iReturn to return 150, also I can mock ToMock to expect a particular method once with supplied argument. But, is it possible to do all of these together in the same time? if yes, how would it look like?

Upvotes: 0

Views: 1681

Answers (2)

localheinz
localheinz

Reputation: 9582

Yes, it is.

Here's an example of a test that covers ToTest:

<?php

use PHPUnit\Framework\TestCase;

class ToTestTest extends TestCase
{
    /**
     * @dataProvider providerTimeBetween0And100
     *
     * @param int $time
     */
    public function testFeedMeWhenTimeIsBetween0And100($time)
    {
        $toMock = $this->createMock(ToMock::class);

        $toMock
            ->expects($this->exactly(2))
            ->method('iReturn')
            ->willReturn($time);

        $toMock
            ->expects($this->never())
            ->method('callMe1');

        $toMock
            ->expects($this->once())
            ->method('callMe2')
            ->with(
                $this->identicalTo(15),
                $this->identicalTo(20)
            );

        $toTest = new ToTest();

        $toTest->feedMe($toMock);
    }

    public function providerTimeBetween0And100()
    {
        return $this->providerTimeBetween(0, 100);
    }

    /**
     * @dataProvider providerTimeBetween101And199
     *
     * @param int $time
     */
    public function testFeedMeWhenTimeIsBetween101And199($time)
    {
        $toMock = $this->createMock(ToMock::class);

        $toMock
            ->expects($this->exactly(2))
            ->method('iReturn')
            ->willReturn($time);

        $toMock
            ->expects($this->once())
            ->method('callMe1')
            ->with(
                $this->identicalTo(5),
                $this->identicalTo(10)
            );

        $toMock
            ->expects($this->once())
            ->method('callMe2')
            ->with(
                $this->identicalTo(15),
                $this->identicalTo(20)
            );

        $toTest = new ToTest();

        $toTest->feedMe($toMock);
    }

    public function providerTimeBetween101And199()
    {
        return $this->providerTimeBetween(101, 199);
    }
    /**
     * @dataProvider providerTimeGreaterThan199
     *
     * @param int $time
     */
    public function testFeedMeWhenTimeIsGreaterThan199($time)
    {
        $toMock = $this->createMock(ToMock::class);

        $toMock
            ->expects($this->exactly(2))
            ->method('iReturn')
            ->willReturn($time);

        $toMock
            ->expects($this->once())
            ->method('callMe1')
            ->with(
                $this->identicalTo(5),
                $this->identicalTo(10)
            );

        $toMock
            ->expects($this->never())
            ->method('callMe2');

        $toTest = new ToTest();

        $toTest->feedMe($toMock);
    }

    public function providerTimeGreaterThan199()
    {
        return $this->providerTimeBetween(200, 300);
    }

    private function providerTimeBetween($min, $max)
    {
        for ($time = $min; $time < $max; ++$time) {
            yield [
                $time
            ];
        }
    }
}

Note how multiple expectations are set up using expects() and further constraints. All of these can be easily combined.

For reference, see:

Upvotes: 1

xmike
xmike

Reputation: 1038

If I understood your question correctly then test code could look like

class MockingExpectationsTest extends PHPUnit_Framework_TestCase
{
    private $obj;
    private $mock;

    public function setUp(){
        $this->mock = $this->createMock(ToMock::class);
        $this->obj = new ToTest();
    }

    public function testExpectations(){
        $this->mock->expects($this->exactly(2))
                   ->method('iReturn')
                   ->willReturn(150);
        $this->mock->expects($this->exactly(1))
                   ->method('callMe1')
                    ->with(5, 10);
        $this->mock->expects($this->exactly(1))
                   ->method('callMe2')
                    ->with(15, 20);
        $this->obj->feedMe($this->mock);
    }
}

Upvotes: 1

Related Questions