Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to change current-time for unit testing date functions in php

How can I change the current time (i.e. the output of time()) in PHP for unit-testing date-manipulation-class?

like image 912
happytoad Avatar asked Nov 19 '10 02:11

happytoad


4 Answers

I recently came up with a solution that is great if you are using PHP 5.3 namespaces. You can implement a new time() function inside your current namespace and create a shared resource where you set the return value in your tests. Then any unqualified call to time() will use your new function.

For further reading I described it in detail in my blog

like image 138
Fabian Schmengler Avatar answered Nov 18 '22 13:11

Fabian Schmengler


Sounds like you need to remove the dependency of time() from your date manipulation class

like image 29
rojoca Avatar answered Nov 18 '22 14:11

rojoca


My guess is you are struggling with your dependencies. It looks like you are using the time() function inside your class while with unit testing you want to make sure you control the output of the methods/classes that provide the time for you. I'd say create a Time class that provides you with the time and mock it into your unit tests.

like image 4
ChrisR Avatar answered Nov 18 '22 15:11

ChrisR


You might consider an interface-based inheritance model:

interface iClock
{
    public function getTime();

}

class SystemClock implements iClock
{
    public function getTime()
    {
        return time();
    }
}

class FakeClock implements iClock
{
    private $time;

    function __construct($time)
    {
        $this->time = $time;
    }

    public function getTime()
    {
        return $this->time;
    }

    // you could have more functions to advance the time, etc.
}

Your own classes that need to work with the current time would take a constructor parameter of the iClock interface type.

class YourOwnClass
{
    private $clock;

    function __construct(iClock $clock)
    {
        $this->clock = $clock;
    }

    function someFunction()
    {
        $now = $this->clock->getTime();

        // whatever
        return $now;
    }
}

Then you would simply inject the type of clock you wanted, depending on whether you were in a unit test or not:

$realWorldInstance = new YourOwnClass(new SystemClock());   
echo $realWorldInstance->someFunction();
$unitTestInstance = new YourOwnClass(new FakeClock(1234));
echo $unitTestInstance->someFunction();

BTW - I use this pattern often in .NET as well. It's also built into the IClock interface provided by Noda Time. I see no reason why this pattern wouldn't work in PHP. You might choose to return a DateTime instead of just the integer timestamp, but the above is the minimal needed to use the pattern.

like image 4
Matt Johnson-Pint Avatar answered Nov 18 '22 14:11

Matt Johnson-Pint