如何在PHPUnit中存根更复杂的方法

use*_*653 2 php testing phpunit unit-testing

我正在尝试减少程序的依赖性,使其更容易测试.我这样做的一个实例是我__construct()的一个类的方法.之前,它曾经接受一个文件名,然后该__construct()方法将使用该文件名将file_get_contents()内容保存到属性中:

public function __construct($name){
  $this->name = $name;
  $this->contents = file_get_contents($name);
}
Run Code Online (Sandbox Code Playgroud)

为了减少对文件系统的依赖,我将其替换为:

public function __construct(SplFileObject $file){
  $this->name = $file->getFilename();
  $this->contents = '';
  while(!$file->eof()){
    $this->contents .= $file->fgets();
  }
}
Run Code Online (Sandbox Code Playgroud)

我相信这更容易测试,因为我可以模拟一个SplFileObject(可以设置为包含我想要的任何内容)并传入它.到目前为止我看到的例子包括做这样的事情:

$stub = $this->getMock('SplFileObject');
$stub->expects($this->any())
     ->method('fgets')
     ->will($this->returnValue('contents of file'));
Run Code Online (Sandbox Code Playgroud)

然而,意志的模拟fgets方法SplFileObject需要更复杂 - 它需要遍历内容的每一行,并在它到达结束时停止.

暂时我有一个有效的解决方案 - 我刚刚创建了一个全新的类MockSplFileObject来覆盖这些方法:

class MockSplFileObject extends SplFileObject{
    public $maxLines;
    public $filename;
    public $contents;
    public $currentLine = 1;

  public function __construct($filename, $contents){
    $this->filename = $filename;
    $this->contents = explode("\n",$contents);
    return true;
  }

  public function eof(){
    if($this->currentLine == count($this->contents)+1){
        return true;
    }
    return false;
  }

  public function fgets(){
    $line = $this->contents[$this->currentLine-1];
    $this->currentLine++;
    return $line."\n";
  }

  public function getFilename(){
    return $this->filename;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我使用它而不是调用PHPUnit的getMock()函数.我的问题是:这是一种合法的做事方式吗?或者有更好的方法来模拟更复杂的方法吗?

nac*_*son 5

$fileObject = $this->getMock('SplFileObject', [], ['php://memory']);

$fileObject
    ->expects($this->any())
    ->method('fgets')
    ->will($this->onConsecutiveCalls('line 1', 'line 2'));
$fileObject
    ->expects($this->exactly(3))
    ->method('eof')
    ->will($this->onConsecutiveCalls(false, false, true));
Run Code Online (Sandbox Code Playgroud)

使用'php://memory'SplFileObject作为参数帮助我避免了当您尝试模拟SplFileObject时出现的以下错误

PHP Fatal error: Uncaught exception 'LogicException' with message 'The parent constructor was not called: the object is in an invalid state'

  • 由于 getMock() 已被弃用,请使用: `$this->getMockBuilder('SplFileObject')->setConstructorArgs(['php://memory'])->getMock();` (2认同)