Skip to content

Instantly share code, notes, and snippets.

@rosstuck
Last active September 24, 2024 14:36
Show Gist options
  • Select an option

  • Save rosstuck/aaaea8513a825ae7a7e4fd85c08f4fc8 to your computer and use it in GitHub Desktop.

Select an option

Save rosstuck/aaaea8513a825ae7a7e4fd85c08f4fc8 to your computer and use it in GitHub Desktop.

Revisions

  1. rosstuck revised this gist Oct 18, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion clock-example.php
    Original file line number Diff line number Diff line change
    @@ -77,7 +77,7 @@ public function daysLeft(DateTimeImmutable $myBirthday): int
    $calculator = new DaysUntilBirthdayCalculator(new SystemClock());
    var_dump($calculator->daysLeft($myBirthday));

    // Oh wait, when I write a unit test for this last month but it constantly fails!
    // Oh wait, I wrote a unit test for this last month but it constantly fails!
    var_dump($calculator->daysLeft($myBirthday) === 34);

    // No worries, let's use a mock clock. Now we can write unit tests that we can rely on always!
  2. rosstuck created this gist Oct 18, 2016.
    87 changes: 87 additions & 0 deletions clock-example.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,87 @@
    <?php

    /**
    * This is the clock interface. It's really simple, you write it once, use it anywhere.
    * Cool extra things you can do:
    * - have it return custom value objects
    * - separate method for currentDate() without time part
    */
    interface Clock
    {
    public function currentTime(): DateTimeImmutable;
    }

    /**
    * SystemClock just relies on the current system:
    *
    * Cool extra things you can do:
    * - force a timezone regardless of the current system settings
    * - settle disputes about DateTimeImmutable vs DateTime in your codebase ONCE AND FOR ALL, MWAHHAHA
    */
    class SystemClock implements Clock
    {
    public function currentTime(): DateTimeImmutable
    {
    return new DateTimeImmutable('now');
    }
    }

    /**
    * Mock Clock just returns a date you set.
    *
    * Cool extra things you can do:
    *
    * - mock current time in unit tests
    * - shortcut constructors like MockClock::isCurrently('10:41')
    * - have a setter to advance the clock as the test progresses
    */
    class MockClock implements Clock
    {
    private $fixedTime;

    public function __construct(DateTimeImmutable $dateTime)
    {
    $this->fixedTime = $dateTime;
    }

    public function currentTime(): DateTimeImmutable
    {
    return $this->fixedTime;
    }
    }

    // Okay, how/why would we use it? Okay, consider this off-the-cuff use case{
    class DaysUntilBirthdayCalculator
    {
    /** @var Clock */
    private $clock;

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

    public function daysLeft(DateTimeImmutable $myBirthday): int
    {
    $currentTime = $this->clock->currentTime();
    if ($currentTime > $myBirthday) {
    throw new LogicException("Hey, you already had that birthday!");
    }

    return (int)$myBirthday->diff($currentTime)->format("%a");
    }
    }

    // Cool, number of days until my birthday!
    $myBirthday = new DateTimeImmutable('Nov 10, 2016');
    $calculator = new DaysUntilBirthdayCalculator(new SystemClock());
    var_dump($calculator->daysLeft($myBirthday));

    // Oh wait, when I write a unit test for this last month but it constantly fails!
    var_dump($calculator->daysLeft($myBirthday) === 34);

    // No worries, let's use a mock clock. Now we can write unit tests that we can rely on always!
    $calculator = new DaysUntilBirthdayCalculator(
    new MockClock(new DateTimeImmutable('2016-10-18'))
    );
    var_dump($calculator->daysLeft($myBirthday) === 23);