Search code examples
phpsymfonytestingcachingphpunit

Correct approach to test token expiration in PhpUnit


I've written service for getting JWT token and caching it for 59 minutes. I'm writing now a tests for this service. In my AuthService i have 2 methods:

public function getToken(): string
    {
        $token = $this->cache->getItem('access_token');
        // Czy jest token w cache
        if ($token->isHit()) {
            return $token->get();
        } else {
            $newToken = $this->getNewToken();
            $this->saveTokenInCache($newToken);
            return $newToken;
        }
    }

private function saveTokenInCache($tokenToSave): void
    {
        $savedToken = $this->cache->getItem('access_token');
        $savedToken->set($tokenToSave);
        $savedToken->expiresAfter(3540);
        $this->cache->save($savedToken);
   }

and I have a test:

 /**
     * @test
     */
    public function new_token_should_be_fetched_after_expiration()
    {
        $this->msGraphAuthService->expects($this->exactly(2))
            ->method('getNewToken');
        // getToken
        $this->msGraphAuthService->getToken();
        // change time
        $date = new DateTime();
        $date->modify('3541 seconds');
        $this->msGraphAuthService->getToken();

    }

For Cache I'm using FileSystemAdapter.

Setup Function with mock of getNewToken method is:

protected function setUp(): void
    {
        $kernel = self::bootKernel();
        $this->cacheService = new FilesystemAdapter();
        $this->serializer = $kernel->getContainer()->get('serializer');
        $this->logger = $this->createMock(Logger::class);
        $this->msGraphAuthService =$this>getMockBuilder(MicrosoftGraphAuthService::class)
            ->onlyMethods(['getNewToken'])
            ->setConstructorArgs([$this->logger, "", "", "", ""])
            ->getMock();
        $this->msGraphAuthService
            ->method('getNewToken')
            ->willReturn('{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"eyJ..."}');
    }

My exact goal in test new_token_should_be_fetched_after_expiration is to check if getNewToken method has been invoked exactly 2 times but how could I move time forward 59 minutes later than now?

I tried to do something like:

 $date = new DateTime();
 $date->modify('3541 seconds');

but it dosen't work.

I would be grateful for help.


Solution

  • It looks like time is a hidden dependency of getNewToken().

    Make the dependency more visible. E.g. be it $_SERVER['REQUEST_TIME'] or a more dedicated $date parameter that defaults to it (or whatever you have in the implementation):

    ...
    $date = new DateTime();
    $token = $this->getNewToken($date);
    ...
    

    You can then easily create tokens that expire soon, have expired already and/or you can remove the hidden dependency of time on the check routine as well.