测试私有属性/方法


Testing private attributes/methods

我有以下方法要测试:

public function getJSONData(ExtClass $ext) {
   $obj = new DataClass();
   $obj -> setData($ext -> something);
   $sendObj = new SendClass();
   $sendObj -> send($obj);
   echo $obj -> getJSONData();
}

该方法根据对象的参数生成一些JSON数据$ext。我可以通过传递不同的 ExtClass 对象来测试它的输出,如下所示:

public function testGetJSONData() {
   $extObj = new ExtObject('aaa', 'bbb', 'ccc');
   $mObj = new MainClass();
   $jsonData = $mObj -> getJSONData($extObj);
   $this -> assertEquals(1, $jsonData -> d);
   // etc
}

但是这样我就无法测试途中发生的所有不同事情——DB 插入、创建的结构等。

例如,假设我有以下DataClass类:

class DataClass {
   private $arr1;
   private $val2;
   public function setData($something) {
      $this -> _setArr();
      $this -> _setVal();
   }
   private function _setArr() {
      $this -> arr1 = array('fdfds');
   }
   private function _setVal() {
      $this -> saveToDatabaseSomething($this -> arr1 -> something);
      $this -> val1 = 'gfgdfgdfgfd';
   }
}

我无法测试这个类,因为一切都是私有的,所有不同的操作都基于一个输入。

测试它的正确方法是什么?

你永远不需要测试私有属性和方法,如果你认为你需要它,你的代码没有足够的可测试性。

测试途中发生的所有不同事情 - DB 插入、创建的结构等。

对于单元测试,你会模拟这些东西(参见:https://phpunit.de/manual/current/en/test-doubles.html)

在您的情况下,看起来您需要模拟SendClass,这似乎做了一些外部事情。但不幸的是,您是在测试的方法中创建它的。将对象创建与对象使用分开是一种很好的做法,例如:

class ClassToTest
{
    private $sendObj;
    public function __construct(SendClass $sendObj)
    {
        $this->sendObj = $sendObj;
    }
    public function getJSONData(ExtClass $ext) {
       $obj = new DataClass();
       $obj -> setData($ext -> something);
       $this->sendObj -> send($obj);
       echo $obj -> getJSONData();
    }
}

然后你可以像这样编写单元测试:

public function testGetJSONData()
{
    $input = new ExtClass('something');
    $expectedSent = new DataClass('something');
    $expectedJson = "{'something':'something'}";
    $mockSendObj = $this->getMock(SendClass::class, ['send']);
    $mockSendObj->expects($this->once())
        ->method('send')
        ->with($expectedSent);
    $subject = new ClassToTest($mockSendObj);
    $actualJson = $subject->getJSONData($input);
    $this->assertEquals($expectedJson, $actualJson);
}

并且SendClass的实际行为仅在该类的另一个单元测试中进行测试。


还有另一种测试类型,您希望测试单元之间的交互,即集成测试。对于这样的测试,您不会嘲笑协作者,但您也不会测试内部结构,例如调用私有方法,而是测试实际写入数据库的内容。