Skip to content

Instantly share code, notes, and snippets.

@teklakct
Last active November 1, 2018 19:31
Show Gist options
  • Save teklakct/f88832189cdfb9631bfa3a2b0e90dbe6 to your computer and use it in GitHub Desktop.
Save teklakct/f88832189cdfb9631bfa3a2b0e90dbe6 to your computer and use it in GitHub Desktop.
Simple example why you should make an interface over DateTime
<?php
declare(strict_types=1);
interface Clock
{
public function now(): \DateTimeInterface;
}
<?php
declare(strict_types=1);
class DoIt
{
private $clock;
private $deps;
public function __construct(Clock $clock, SomeDependency $deps)
{
$this->clock = $clock;
$this->deps = $deps;
}
public function doSomething(): void
{
if ($this->clock->now() > (new \DateTimeImmutable('2018-11-01 01:00:00'))) {
$this->deps->run();
}
}
}
<?php
declare(strict_types=1);
use PhpSpec\ObjectBehavior;
class DoItSpec extends ObjectBehavior
{
private $now;
public function let(Clock $clock, SomeDependency $deps)
{
$this->beConstructedWith($clock, $deps);
}
public function it_do_something_depends_on_time(Clock $clock, SomeDependency $deps)
{
$clock->now()->willReturn((new \DateTimeImmutable('2020-12-01 01:00:00')));
$deps->run()->shouldBeCalled();
$this->doSomething();
}
}
<?php
declare(strict_types=1);
final class MockableClock implements Clock
{
private $currentTime = null;
private $clock;
public function __construct(Clock $clock)
{
$this->clock = $clock;
}
public function now(): \DateTimeInterface
{
if ($this->currentTime) {
return $this->currentTime;
}
return $this->clock->now();
}
public function set(\DateTimeInterface $time): void
{
$this->currentTime = $time;
}
public function reset(): void
{
$this->currentTime = null;
}
}
<?php
declare(strict_types=1);
final class Something
{
private $clock;
public function __construct(Clock $clock)
{
$this->clock = $clock;
}
/**
* Good way. It easily testable
*/
public function beforeMidday(): bool
{
return (int) $this->format('Hi') < 1200);
}
/**
* This method cannot be tested. It depends on time when test is run.
*/
public function afterMidday(): bool
{
return (int) (new \DateTimeImmutable())->format('Hi') > 1200);
}
}
<?php
declare(strict_types=1);
class SomethingTest
{
/**
* @dataProvider getTestCases
* @test
*/
public function checks_that_current_time_is_before_midday($expected, $currentTime)
{
$systemClock = new SystemClock();
$clock = new MockableClock($systemClock);
$something = new Something($clock);
$something->set($currentTime);
$this->assertEquals($something->beforeMidday(), $expected);
}
public function getTestCases()
{
return [
[true, (new \DateTimeImmutable('2018-11-01 01:00:00'))],
[true, (new \DateTimeImmutable('2018-11-01 11:59:59'))],
[false, (new \DateTimeImmutable('2018-11-01 12:00:00'))],
[false, (new \DateTimeImmutable('2018-11-01 22:22:22'))],
]
}
}
<?php
declare(strict_types=1);
final class SystemClock implements Clock
{
public function now(): \DateTimeInterface
{
return new \DateTimeImmutable();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment