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


PHP: How to make testable static methods

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

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

  1. 使每个查询方法都接收一个连接obect:但这意味着,当我试图在BaseModel类中使用这些方法时,我仍然必须将连接对象紧密耦合到BaseModel
  2. 保持原样:这意味着在PHPUnit测试期间,我必须覆盖BaseModel

不管怎样,我觉得我做的事情不对。我相信有更好的方法可以让这项工作,我需要你们的帮助。感谢

在Statics中引用Miško Hevery是可测试性的死亡:

这是另一种思考方式。单元测试需要接缝,接缝是我们阻止正常代码路径执行的地方,也是我们实现被测试类隔离的方式。seams通过多态性工作,我们覆盖/实现类/接口,然后以不同的方式连接测试中的类,以控制执行流。对于静态方法,没有什么可重写的。是的,静态方法很容易调用,但如果静态方法调用另一个静态方法,则无法覆盖被调用的方法依赖关系。

引用Kore Nordmann在被认为有害的静态:

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

TL;DR:如果您想要可测试的代码,就不要使用statics。

关于您的具体案例:

如果您不想重写代码以不使用statics,请考虑在基类上为查询类添加一个属性,例如添加之类的内容

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

然后将对CCD_ 1的所有硬编码调用替换为对集合CCD_。虽然这仍然有点难看,但它允许您在测试期间用不同的类替换StaticSQLQuery,例如,使用PHP7和Anonymous类,您可以将其放入测试中:

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

为了测试静态方法,我创建了自己的mocking库: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']]