Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save xthiago/6e38841585991a1964d184462d7fd0ab to your computer and use it in GitHub Desktop.
Save xthiago/6e38841585991a1964d184462d7fd0ab to your computer and use it in GitHub Desktop.
Alternatives to test classes that rely on system time.

Value object that depends on system time:

<?php

declare(strict_types=1);

namespace Xthiago;

final class BirthDate
{
    private DateTimeImmutable $date;

    protected function __construct(DateTimeImmutable $date)
    {
        $this->date = $date;
    }

    public function age(Clock $clock) : int
    {
        $interval = $this->date->diff($clock->now());

        return $interval->y;
    }
}

Clock service interface - where the Value Object rely on:

<?php

declare(strict_types=1);

namespace Xthiago;

use DateTimeImmutable;

interface Clock
{
    public function now() : DateTimeImmutable;
}

FrozenClock - implementation of service for tests

<?php

declare(strict_types=1);

namespace Xthiago;

use DateTimeImmutable;

class FrozenClock implements Clock
{
    private DateTimeImmutable $date;

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

    public function now(): DateTimeImmutable
    {
        return $this->date;
    }
}

SystemClock - implementation of service that rely on system clock

<?php

declare(strict_types=1);

namespace Xthiago;

use DateTimeImmutable;

class SystemClock implements Clock
{
    public function now() : DateTimeImmutable
    {
        return new DateTimeImmutable();
    }
}

Using it:

$birthDate = new BirthDate(new DateTimeImmutable('1988-96-14'));
$clock = new SystemClock();
$birthDate->age($clock)

Writting unit tests:

// ..
$birthDate = new BirthDate(new DateTimeImmutable('1988-96-14'));
$clock = new FrozenClock(new DateTimeImmutable('2019-07-16'));
$birthDate->age($clock)

Value object that depends on system time:

<?php

declare(strict_types=1);

namespace Xthiago;

final class BirthDate
{
    private DateTimeImmutable $date;

    protected function __construct(DateTimeImmutable $date)
    {
        $this->date = $date;
    }

    public function age() : int
    {
        $interval = $this->date->diff($this->clock->now());

        return $interval->y;
    }
    
    private function now() : DateTimeImmutable
    {
        return Clock::now();
    }
}

Clock service:

<?php

declare(strict_types=1);

namespace Xthiago;

use DateTimeImmutable;

final class Clock
{
    public static ?DateTimeImmutable $frozenDate;

    public static function now() : DateTimeImmutable
    {
        if (static::$frozenDate instanceof DateTimeImmutable) {
            return static::$frozenDate;
        }

        return new DateTimeImmutable();
    }

    public static function freezeDate(DateTimeImmutable $at) : void
    {
        static::$frozeDate = $at;
    }

    public static function unfreeze() : void
    {
        static::$frozeDate = null;
    }
}

Using it:

$birthDate = new BirthDate(new DateTimeImmutable('1988-96-14'));
$birthDate->age()

Writting unit tests:

// ..
$birthDate = new BirthDate(new DateTimeImmutable('1988-96-14'));
Clock::freezeDate('2019-07-16');
$birthDate->age($clock);

Value object that depends on system time:

<?php

declare(strict_types=1);

namespace Xthiago;

class BirthDate
{
    private DateTimeImmutable $date;

    protected function __construct(DateTimeImmutable $date)
    {
        $this->date = $date;
    }

    public function age() : int
    {
        $interval = $this->date->diff($this->clock->now());

        return $interval->y;
    }
    
    private function now() : DateTimeImmutable
    {
        return new DateTimeImmutable();
    }
}

Using it:

$birthDate = new BirthDate(new DateTimeImmutable('1988-96-14'));
$birthDate->age()

Writting unit tests:

// ..
$birthDate = new BirthDate(new DateTimeImmutable('1988-96-14'));
$birthdate = new class extends BirthDate {
    private function now() : DateTimeImmutable {
        return new DateTimeImmutable('2019-07-16');
    }
};
$birthDate->age($clock);
@nelsonsar
Copy link

nelsonsar commented Jul 16, 2019

Um possível approach pra facilitar seus testes é:

<?php

declare(strict_types=1);

namespace Xthiago;

final class BirthDate
{
    private DateTimeImmutable $date;

    protected function __construct(DateTimeImmutable $date)
    {
        $this->date = $date;
    }

    public function ageAt(DateTimeImmutable $at) : int
    {
        $interval = $this->date->diff($at);

        return $interval->y;
    }
}

Uso:

$birthDate = new BirthDate(new DateTimeImmutable('1988-96-14'));
$birthDate->ageAt(new DateTimeImmutable());

Nos testes você pode usar instâncias de DateTimeImmutable pra datas e idades conhecidas. Acho mais simples mas, talvez não seja o que você precisa 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment