如何测试使用缓存变量的方法?

时间:2017-06-30 23:15:43

标签: php unit-testing yii2 phpunit codeception

<?php

use yii\db\ActiveRecord;

class Model extends ActiveRecord
{
    protected static $ids = [];

    public static function getIds()
    {
        if (empty(static::$ids)) {
            static::$ids = static::find()->select('id')->column();
        }

        return static::$ids;
    }
}

如何通过反复调用此方法来确保查询执行一次?

最好使用codeception或phpunit。

1 个答案:

答案 0 :(得分:3)

测试不仅是确保代码工作的一种方式,还有助于识别代码异味。在你的情况下编写测试很难,因为你使用静态方法。

曾经有一个staticExpects方法,但很久以前在phpunit中已经弃用了,所以这不太可行。使此代码可测试的最佳方法是删除static关键字。对于getIds()来说这很容易,但由于静态find()是由第三方(yii的ActiveRecord)定义的,因此您无法真正删除它。相反,你可以用非静态方法包装它。通过触摸包含第三方代码的这些小方法,这使您能够从将Active Record转移到Doctrine之类的其他实现中。

执行此操作后,您可以创建模型的部分模拟,以确保调用该方法:

class Model extends ActiveRecord
{
    private $ids;

    protected function findIds()
    {
        return static::find()->select('id')->column();
    }

    public function getIds()
    {
        if (empty($this->ids)) {
            $this->ids = $this->findIds()
        }

        return $this->ids;
    }
}

并在你的测试中:

public function testFindIdsIsCalledWhenGetterIsNotInitialized()
{
    $model = $this->getMockBuilder(Model::class)
        ->setMethods(['findIds'])
        ->getMock();
    $model->expects($this->once())
        ->method('findIds')
        ->will($this->returnValue([1, 2, 3]));

    $ids = $model->getIds();

    $this->assertEquals([1, 2, 3], $ids);
}

这应该有2个断言,一个用于预期的方法调用,一个用于返回的值。此测试绕过活动记录,仅确保您的getIds()方法按预期工作。另一种解决方法是,如您的问题评论中所述,使用功能测试,通过从(测试)数据库中获取数据来实际测试数据库交互。显然,因为这需要具有数据库连接并检索测试数据,例如,从一些以前设置的灯具,它是更多的工作,测试将更慢。根据您的项目有多大可能不是问题而且您可能更愿意在Active Record实现中测试逻辑。