模拟包装时间函数

I have a created a minor wrapper function for the php time() method in my class

class MyClass
{
    public function myFuncion($unixTimeStamp)
    {
        $pending = $this->isPending($unixTimeStamp)

        // data manipulation

        return $result
    }

    protected function isPending($unixTimeStamp)
    {
        if ($unixTimeStamp > $this->currentTime()) {
            return true;
        }

        return false;
    }

    public function currentTime()
    {
        return time();
    }
}

I want to test the public function myFunction() in this class but I am at a bit of a loss how I can mock the currentTime method without mocking the SUT itself (MyClass)

What is the correct way of doing this? I felt creating a time class with a single method (getCurrentTime) and injecting it into MyClass while correct, was excessive as I only check the time in one place in my code.

Is this the best method regardless?

EDIT: I am considering making a time trait as it looks like PhpUnit can mock this. Still unsure if this is overkill for use in a single method..

What other options do I have?

You can create a Partial mock object of your tested class in order to modify the behaviour only of the selected method (the currentTime method). For this purpose you can use the setMethods of the Mock Builder API:

setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(NULL), then no methods will be replaced.

So try this code (suppose the myFunction return the result of the isPending method):

class MyClassTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @test
     */
    public function itShouldReturnTrueIfPendingState()
    {
        $currentTime = (new \DateTime('now -1 year'))->getTimestamp();

        /** @var MyClass|\PHPUnit_Framework_MockObject_MockObject $myClass */
        $myClass = $this->getMockBuilder(MyClass::class)
            ->disableOriginalConstructor()
            ->setMethods(['currentTime'])
            ->getMock();

        $myClass
            ->method('currentTime')
            ->willReturn($currentTime);

        $this->assertTrue($myClass->myFunction(time()));
    }

    /**
     * @test
     */
    public function itShouldReturnFalseIfNotState()
    {
        $currentTime = (new \DateTime('now +1 year'))->getTimestamp();


        /** @var MyClass|\PHPUnit_Framework_MockObject_MockObject $myClass */
        $myClass = $this->getMockBuilder(MyClass::class)
            ->disableOriginalConstructor()
            ->setMethods(['currentTime'])
            ->getMock();

        $myClass
            ->method('currentTime')
            ->willReturn($currentTime);

        $this->assertFalse($myClass->myFunction(time()));
    }

We can override the time() function in the namespace level. Check https://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/

but the downfall is that every time we call time() it will return the mocked time :)

namespace MyClass;

function time()
{
    return MyClassTest::$mockTime ?: \time();
}

class MyClassTest extends TestCase{

    public static $mockTime;

    public function test_my_function(){   
        self::$mockTime = '08:10';
        $myClass = new MyClass();
        //$myClass->currentTime() //will return 08:10
        //do something with my function
    }