【问题标题】:Correct approach to test token expiration in PhpUnit在 PhpUnit 中测试令牌过期的正确方法
【发布时间】:2021-08-04 15:55:52
【问题描述】:

我已经编写了获取 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 分钟呢? p>

我试图做类似的事情:

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

但它不起作用。

我将不胜感激。

【问题讨论】:

    标签: php symfony testing caching phpunit


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 我从外部 API 接收令牌。现在我正在尝试使用 symfony 计时器模拟:symfony.com/doc/current/components/…
    • 如果时间在两个系统之间共享并且您无法注入它,并且您想测试令牌过期,您必须进行测试,保持每个滴答的笔记并接收足够蜱。然后,您可以比较行为是否符合您的期望。例如。 sleep() 等。这不是单元测试,属于不同的套件。我会以两倍于预期到期时间的时间运行此测试。或者,在准备中创建一个您知道将在测试中过期的令牌,并将其仅用于预期过期的测试(夹具是过期的令牌)。同样不是严格意义上的单元测试。
    • 我正在设置过期时间,如下所示: $savedToken = $this->cache->getItem('access_token'); $savedToken->set($tokenToSave); $savedToken->expiresAfter(3540); $this->cache->save($savedToken);无论是否是单元测试,这对我来说都不重要。
    • 好的,那我误读了你的评论。然后你为什么不测试a)-1秒,b)0秒和c)1秒?然后,您可以处理结果并确定您的初始期望是否反映在 api 中。
    【解决方案2】:

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

        /**
         * @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 类会调用来自ClockMock 的假方法。在第二行中,我在 24 小时前设置了当前时间。这会导致该令牌立即过期。

    【讨论】:

      猜你喜欢
      • 2012-05-13
      • 1970-01-01
      • 2018-03-27
      • 1970-01-01
      • 2018-11-21
      • 1970-01-01
      • 2019-02-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多