一尘不染

用PHPUnit模拟私有方法

php

我有一个关于使用PHPUnit模拟类中的私有方法的问题。让我举一个例子:

class A {
  public function b() { 
    // some code
    $this->c(); 
    // some more code
  }

  private function c(){ 
    // some code
  }
}

我该如何对私有方法的结果进行存根测试以测试公共函数的 更多代码 部分。


阅读 322

收藏
2020-05-26

共1个答案

一尘不染

通常,您只是不直接测试或嘲笑私有和受保护的方法。

您要测试的是您的类的 公共 API。其他所有内容都是您的类的实现细节,并且如果更改它,则不应“破坏”您的测试。

当您发现“无法获得100%的代码覆盖率”时,这也对您有所帮助,因为您的类中可能包含无法通过调用公共API执行的代码。


您通常不想这样做

但是,如果您的课程如下所示:

class a {

    public function b() {
        return 5 + $this->c();
    }

    private function c() {
        return mt_rand(1,3);
    }
}

我可以看到需要模拟c()的需求,因为“随机”函数是全局状态,您无法对其进行测试。

“干净” /“冗长” //可能过于复杂//“通常”

class a {

    public function __construct(RandomGenerator $foo) {
        $this->foo = $foo;
    }

    public function b() {
        return 5 + $this->c();
    }

    private function c() {
        return $this->foo->rand(1,3);
    }
}

现在,不再需要模拟“ c()”,因为它不包含任何全局变量,并且可以很好地进行测试。


如果您不想执行或无法从私有函数中删除全局状态(不好的事情,坏现实,或者您对坏的定义可能有所不同),则 可以 对模拟 进行 测试。

// maybe set the function protected for this to work
$testMe = $this->getMock("a", array("c"));
$testMe->expects($this->once())->method("c")->will($this->returnValue(123123));

并针对此模拟运行测试,因为您取出/模拟的唯一函数是“ c()”。


引用“实用单元测试”这本书:

“总的来说,您不想为了测试而破坏任何封装(或者就像妈妈曾经说过的那样,“不要公开您的私人信息!”)。大多数时候,您应该能够测试一门课程通过行使其公开方法。如果在私有访问或受保护的访问后面隐藏着重要的功能,则可能是一个警告信号,表明其中还有另一个类别正在努力摆脱困境。”


2020-05-26