如何对单元用私有构造函数调用外部类的函数进行PHP单元测试

时间:2018-07-29 01:15:47

标签: php phpunit-testing

这是我正在使用的代码的简化版本:

class FirstClass{
    public $theSecondObject;
    function exampleForTesting(){
        $this->theSecondObject = SecondClass::getInstance();
        return $this->theSecondObject->getRandomSentence();
    }
}

class SecondClass{
    private static $instance = null;
    private $randomSentence;
    private function __construct($someString){
        $this->randomSentence = $someString;
    }
    public static function getInstance(){
        if(self::instance === null){
            self::$instance = new SecondClass('Just a random sentence.');
        }
        return self::$instance;
    }
    public function getRandomSentence(){
        return $this->randomSentence;
    }
}

class FirstClassTestCase {
    public function startTest($method){
        $this->firstClassObject = new FirstClass();
    }
    public function testExampleForTesting(){
        $this->firstClassObject->theSecondObject = $this->getMock('SecondClass', ['getRandomSentence']);
        $this->firstClassObject->theSecondObject->expects($this->any()
            ->method('getRandomSentence')
            ->will($this->returnValue('A test unexpected sentence.'));
        $expected = 'Just a random sentence.';
        $actual = $this->firstClassObject->exampleForTesting();
        $this->assertNotEquals($expected, $actual);
    }
}

我有一个带有函数exampleForTesting()的FirstClass,该函数调用SecondClass()函数,我想模拟该函数的返回值以进行测试。但是,SecondClass()构造函数是私有的,运行测试结果将引发错误:

Fatal Error Error:  Call to private SecondClass::__construct()

这似乎是由于通过getInstance()函数实例化SecondClass对象的独特方式。有没有解决这个问题的潜在方法,而不必更改SecondClass构造函数? SecondClass实际上是我的应用程序中的一个外部调用,因此我无法更改它。

我还注意到,即使我试图将构造函数设置为public以查看测试是否会通过,它实际上也会失败,因为$ actual变量会生成与$ expected相同的值。我是否错误地执行了返回值模拟?

1 个答案:

答案 0 :(得分:1)

一种选择是更改您的实现并使用名为dependency injection的东西。

@NgModule({
  declarations: [ProductPageComponent
..
  ],
  entryComponents: [ProductPageComponent],
  imports: [
..
  ],
  providers: [...],
  bootstrap: [AppComponent]
})

这样,您可以在测试中轻松模拟class FirstClass { public function __construct(SecondClass $secondClass) { //... } } 并将其作为参数传递给SecondClass

FirstClass

有时,包装不属于您的类也很有意义,因为这样做通常会给您带来更大的灵活性。