How to "Mock" Time in PHPunit

Can I Mock time in PHPUnit?

I recently came up with another 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

How to mock date in php unit test?

Unit testing works best by avoiding side effects. Both date and strtotime are depending on an external state defined on your host system, namely the current time.

One way to deal with that is to make current time an injectable property allowing you to "freeze" it or to set it to a specific value.

If you look at at the definition to strtotime it allows setting current time:

strtotime ( string $time [, int $now = time() ] ) : int

Same with date:

date ( string $format [, int $timestamp = time() ] ) : string

So always inject that value from your function to decouple the results of your code from your host's state.

function changeStartEndDate($now) {

if (date('j', strtotime("now", $now), $now) === '1') {
...
$this->startDate = date("Y-n-j", strtotime(date("Y-m-01", $now), $now));
$this->endDate = date("Y-n-j", strtotime("yesterday", $now), $now);
}

Is your function part of a class? I would then make the $now part of the constructor, and have it default to time(). In your test cases, you can always inject a fixed number, and it will always return the same output.

class MyClassDealingWithTime {
private $now;

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

private customDate($format) {
return date($format, $this->now);
}

private customStringToTime($timeSring) {
return strtotime($timeStrimg, $this->now);
}
}

In your test cases you then set $now to the value you need, e.g. via

$firstDayOfAMonth = (new DateTime('2017-06-01'))->getTimestamp();
$testInstance = new MyClassDealingWithTime(firstDayOfAMonth);

$actual = $testInstance->publicMethodYouWantTotest();

...

How to fake DateTime in a phpunit test?

Following the link you included in your post, the first paragraph says:

The ClockMock class provided by this bridge allows you to mock the
PHP's built-in time functions time(), microtime(), sleep() and
usleep(). Additionally the function date() is mocked so it uses the
mocked time if no timestamp is specified. Other functions with an
optional timestamp parameter that defaults to time() will still use
the system time instead of the mocked time
.
(Emphasis added.)

This means that your call of

echo (new DateTime())->format('Y-m-d H:m:s');

is expected to give the system time, not the mocked time.

Change it to

echo date('Y-m-d H:m:s');

in order to match the requirements of ClockMock and get the mocked time.

Note: I've never used ClockMock myself, but just looking at the documentation this should be a first step to see if this resolves your problem.

How to mock time() function while testing in Laravel?

I think Carbon should be used instead of time():

Carbon::now()->timestamp // Or just now()->timestamp in 5.5+

You can easily mock Carbon instances.

If you don't use time() a lot, you could also create your own helper:

function timestamp()
{
if (app()->runningUnitTests()) {
return ....
} else {
return time();
}
}

And use it instead of time():

timestamp()

Mocking The Time used by all instances of DateTime for testing purposes

You should stub the DateTime methods you need in your tests to return expected values.

$stub = $this->getMock('DateTime');
$stub->expects($this->any())
->method('theMethodYouNeedToReturnACertainValue')
->will($this->returnValue('your certain value'));

See https://phpunit.de/manual/current/en/test-doubles.html

If you cannot stub the methods because they are hardcoded into your code, have a look at

  • Stubbing Hard-Coded Dependencies by Sebastian Bergmann

which explains how to invoke a callback whenever new is invoked. You could then replace the DateTime class with a custom DateTime class that has a fixed time. Another option would be to use http://antecedent.github.io/patchwork

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

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:

  • https://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mock-objects
  • https://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mock-objects.tables.matchers

Mocking a wrapper time function

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()));
}

PHPUnit mocking - fail immediately when method called x times

I managed to find a solution in the end. I used a comination of $this->returnCallback() and passing the PHPUnit matcher to keep track of the invocation count. You can then throw a PHPUnit exception so that you get nice output too:

$matcher = $this->any();
$mock
->expects($matcher)
->method('execute')
->will($this->returnCallback(function() use($matcher) {
switch ($matcher->getInvocationCount())
{
case 0: return 'foo';
case 1: return 'bar';
case 2: return 'baz';
}

throw new PHPUnit_Framework_ExpectationFailedException('Called too many times.');
}))
;

Unit testing a function containing DateTime(now)

You could instead pass in the $date_start variable to the function, and when you call it in your test it would be the same every time ie

function testMymethod(){
$date_start = new DateTime('2011-01-01');
$class->getSomeInfo($id, $date_start);
//assertions
}

And your method signiture would change to:

    class myclass {
public function getSomeInfo($id, $date_start = new DateTime()){...}
}


Related Topics



Leave a reply



Submit