在 PhpUnit 中测试令牌过期的正确方法

Correct approach to test token expiration in PhpUnit

我已经编写了获取 JWT 令牌并将其缓存 59 分钟的服务。我现在正在为此服务编写测试。 在我的 AuthService 中,我有 2 种方法:

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

我有一个测试:

 /**
     * @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();

    }

对于缓存,我正在使用 FileSystemAdapter

模拟 getNewToken 方法的设置函数是:

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..."}');
    }

我在测试 new_token_should_be_fetched_after_expiration 中的确切目标是检查 getNewToken 方法是否被恰好调用了 2 次,但是怎么可能我把时间往前拨了59分钟?

我尝试做类似的事情:

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

但它不起作用。

我将不胜感激。

看起来时间是 getNewToken() 的隐藏依赖项。

使依赖关系更加明显。例如。是 $_SERVER['REQUEST_TIME'] 还是更专用的 $date 默认参数(或您在实现中拥有的任何参数):

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

然后您可以轻松创建即将过期的令牌,已经过期的令牌and/or您也可以删除时间对检查例程的隐藏依赖性。

幸运的是我找到了解决方案:

    /**
     * @test
     *
     */
    public function new_token_should_be_fetched_again_after_expiration()
    {
        ClockMock::register(CacheItem::class);
        ClockMock::withClockMock(microtime(true) - 3600 * 24);
        // should be invoked exactly 2 times
        $this->msGraphAuthService->expects($this->exactly(2))
            ->method('getNewToken');
        // getToken
        $this->msGraphAuthService->getToken();
        $this->msGraphAuthService->getToken();
    }

行数:

ClockMock::register(CacheItem::class);
ClockMock::withClockMock(microtime(true) - 3600 * 24);

因为 CacheItem class 将从 ClockMock 调用假方法。在第二行中,我将当前时间设置为 24 小时前。它导致令牌立即过期。