简体   繁体   English

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

[英]How to test a method that uses the cache variable?

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

How to use the test to make sure that the query is executed once by repeatedly calling this method ? 如何通过重复调用此方法使用测试来确保查询执行一次?

Preferably using codeception or phpunit. 最好使用codeception或phpunit。

Tests are not just a way to ensure your code works, they also help identify code smells. 测试不仅是确保代码正常工作的一种方式,而且还有助于识别代码的味道。 In your case writing a test is hard, because you use static methods. 就您而言,编写测试非常困难,因为您使用静态方法。

There used to be a staticExpects method but that was deprecated in phpunit long ago, so that's not really feasible. 曾经有一个staticExpects方法,但是staticExpects在phpunit中就已弃用了它,因此这实际上是不可行的。 The best way to make this code testable is to remove the static keyword. 使此代码可测试的最佳方法是删除static关键字。 This is easy for getIds() but since the static find() is defined by a 3rd party (yii's ActiveRecord) you can't really remove it. 对于getIds()这很容易,但是由于静态find()是由第三方(yii的ActiveRecord)定义的,因此您无法真正删除它。 Instead you could wrap it in a non-static method. 相反,您可以将其包装在非静态方法中。 This gives you the benefit of being able to move away from the Active Record to some other implementation like Doctrine in the future, by just touching these small methods wrapping the 3rd party code. 这给您带来的好处是,将来只需触摸包装第三方代码的这些小方法,就可以从Active Record转移到其他实现,例如Doctrine。

Once you do this you could create a partial mock of your model to make sure that method is called: 完成此操作后,您可以创建模型的部分模拟,以确保调用该方法:

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

and in your test: 并在您的测试中:

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

This should have 2 assertions, one for the expected method call and one for the returned values. 它应该有2个断言,一个用于预期的方法调用,一个用于返回值。 This test bypasses the Active Record and only ensures that your getIds() method works as expected. 此测试绕过Active Record,仅确保您的getIds()方法按预期工作。 Another way to approach this is, as mentioned in the comments to your question, to use a functional test that actually tests the database interactions by fetching the data from a (test) database. 解决此问题的另一种方法是,如对问题的评论中所述,使用功能测试,该功能测试通过从(测试)数据库中获取数据来实际测试数据库交互。 Obviously since this requires having a database connection and retrieving test data, eg from some previously setup fixtures, it's a bit more work and the test will be slower. 显然,由于这需要建立数据库连接并从例如以前安装的固定装置中检索测试数据,因此这需要更多的工作,并且测试速度会变慢。 Depending on how big your project is that might not be an issue and you might feel more comfortable testing the logic in the Active Record implementation as well. 取决于项目的大小,这可能不是问题,并且您也可能会更轻松地测试Active Record实现中的逻辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM