PHP:如何创建可测试的静态方法

PHP:如何创建可测试的静态方法,php,unit-testing,oop,static,Php,Unit Testing,Oop,Static,我决定制作自己的穷人ORM框架。因为我不知道该怎么做,所以我决定在测试前开发。在按照我想要的方式工作之后,我意识到我的代码是不稳定的,因为来自查询类和BaseModel的静态方法之间存在紧密耦合 所以我想了一些方法让它可以测试: 使每个查询方法都接收一个连接对象:但这意味着,当我尝试在BaseModel类中使用这些方法时,我仍然必须将连接对象与BaseModel紧密耦合 保持原样:这意味着在PHPUnit测试期间,我必须重写BaseModel 不管怎样,我觉得我做的事情不对。我相信有一个更好的方

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

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

  • 使每个查询方法都接收一个连接对象:但这意味着,当我尝试在BaseModel类中使用这些方法时,我仍然必须将连接对象与BaseModel紧密耦合
  • 保持原样:这意味着在PHPUnit测试期间,我必须重写BaseModel
  • 不管怎样,我觉得我做的事情不对。我相信有一个更好的方法可以让这一切顺利进行,我需要你们的帮助。谢谢你引用米什科·海弗利的话:

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

    引用Kore Nordmann的话:

    如果您开始对代码进行单元测试,您会很快注意到为什么静态依赖性如此糟糕,因为在测试期间几乎不可能替换实现,因此您通常会测试整个框架堆栈,或者仅仅为了可测试性而需要编写几十个帮助器类。如果没有静态依赖项,您就不需要它

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

    关于你的具体情况:

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

    的属性。
    public static function setQueryClass($className) {
        static::queryClass = $queryClass;
    }
    
    然后将对
    StaticSQLQuery
    的所有硬编码调用替换为对set
    $className
    的调用。虽然这仍然有点难看,但它允许您在测试期间使用不同的类替换
    StaticSQLQuery
    ,例如,您可以将其放入测试中:

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

    为了测试静态方法,我创建了自己的模拟库:

    下面是如何使用它来测试静态代码的快速示例:

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

    快看一下你的代码,看看下面的文章。你可能需要重构一些东西。好的应用程序从技术设计开始,以避免出现问题并轻松实现单元测试等附加功能。我认为这个问题可能更适合codereview.stackexchange.com,而不是堆栈溢出。在回答您的问题时,由于许多原因,您基本上不能对依赖于静态的代码进行单元测试。您最好尽可能避免使用静态方法,并依赖其他技术,如依赖项注入(传入内容)。静态有时会有用途,但总的来说,它们造成的问题比解决的问题要多。@GordonM,是的,你是绝对正确的,如果你看我的第一个建议,它是针对依赖注入的,但最终必须消耗静态方法,这最终会导致紧密耦合。如果仔细观察,您会发现使用静态方法是有意义的,尤其是在使用BaseModel::getAll()时