PHP:如何制作可测试的静态方法

时间:2015-12-20 10:57:45

标签: php unit-testing oop static

我决定制作自己的穷人的ORM框架。由于我不知道如何去做,我决定在测试之前开发。 Github。在按照我想要的方式工作之后,我意识到我的代码是不可测试的,因为查询类和BaseModel之间的静态方法之间存在紧密耦合。

所以我想到了一些让它可以测试的方法:

  1. 让每个查询方法接收一个连接obect:但这意味着当我尝试在BaseModel类中使用这些方法时,我仍然需要将连接对象紧密地耦合到BaseModel。
  2. 保持原样:这意味着在PHPUnit测试期间,我将不得不覆盖BaseModel。
  3. 无论哪种方式,我觉得我做的不对。我相信我有更好的方法来完成这项工作,我需要你的帮助。感谢

2 个答案:

答案 0 :(得分:2)

Statics are Death to Testability中引用MiškoHevery:

  

这是另一种思考方式。单元测试需要接缝,接缝是我们阻止正常代码路径执行的地方,也是我们如何实现被测试类的隔离。接缝通过多态实现,我们覆盖/实现类/接口,而不是以不同的方式连接测试类以控制执行流。使用静态方法无需覆盖。是的,静态方法很容易调用,但如果静态方法调用另一个静态方法,则无法覆盖被调用的方法依赖项。

Static considered harmful中引用Kore Nordmann:

  

如果你开始对代码进行单元测试,你会很快发现为什么静态依赖是如此糟糕 - 因为在测试过程中几乎不可能替换实现,所以你经常最终测试你的完整框架堆栈或需要编写几十个辅助类只是为了可测试性。如果没有静态依赖,你就不需要它。

TL; DR:如果您需要可测试的代码,请不要使用静态。

关于您的具体案例:

如果您不想重写代码以不使用静态,请考虑在基类上添加查询类的属性,例如:添加类似

的内容
public static function setQueryClass($className) {
    static::queryClass = $queryClass;
}

然后通过调用set StaticSQLQuery将所有硬编码调用替换为$className。虽然这仍然有点难看,但它允许您在测试期间用不同的类替换StaticSQLQuery,例如with PHP7 and Anonymous classes你会把它放到你的测试中:

BaseClass::setQueryClass(
    get_class(new class extends StaticSQLQuery {
        public static function init()
        {
            return 'My stub';
        }
    })
);

在PHP7之前,您将不得不对该类进行硬编码:

class StaticSQLQueryMock extends StaticSQLQuery
{
    public static function init()
    {
        return 'My stub';
    }
}

BaseClass::setQueryClass(StaticSQLQueryMock::class);

答案 1 :(得分:0)

我做了自己的模拟库,以测试静态方法:https://github.com/shagabutdinov/moka

以下是如何使用它来测试静态代码的简单示例:

$classMock = \shagabutdinov\Moka::mockClass('MyClass', ['::method' => 'RESULT']);
$classMock::method('ARG1'); // RESULT
$classMock::method('ARG2'); // RESULT
$classMock::moka->report('method'); // [['ARG1', 'ARG2']]