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
}