我有以下方法要测试:
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
的实际行为仅在该类的另一个单元测试中进行测试。
还有另一种测试类型,您希望测试单元之间的交互,即集成测试。对于这样的测试,您不会嘲笑协作者,但您也不会测试内部结构,例如调用私有方法,而是测试实际写入数据库的内容。