【问题标题】:Testing fgets returning false测试 fgets 返回 false
【发布时间】:2014-11-19 22:00:13
【问题描述】:

我正在尝试为一个看起来有点像这样的函数编写单元测试:

function read($stream) {

  $line = fgets($stream);
  if (feof($stream)) {
     throw EofException('...');
  }
  if ($line === false) {
     throw new ReadException('Stream error!');
  }
  return $line;

}

调用此函数的一种方法是:

$h = fopen(__FILE__,'r');
$line = read($h);

我试图弄清楚如何模拟fgets 在我们没有到达文件末尾的情况下返回false。我想为ReadException 案例编写一个单元测试。

这可能吗?

【问题讨论】:

  • while(($line = fgets($stream)) !== FALSE) { /*do your stuff*/ } 你必须阅读所有行,直到到达 EOF。

标签: php unit-testing stream phpunit


【解决方案1】:

如果您正在测试的代码在命名空间中,您可以使用这个技巧:

namespace App {

    //fgets is a "mock" for the one from global namespace
    function fgets($handle, $length = 1024) {
        return false;
    }

    class ReadException extends \Exception {

    }

    function read($stream) {
        //this calls the function in the namespace because it exists
        //otherwise falls back to the global one
        $line = fgets($stream);

        if (feof($stream)) {
            throw EofException('...');
        }

        if ($line === false) {
            throw new ReadException('Stream error!');
        }

        return $line;
    }

}

那么您在全局命名空间中的测试将如下所示:

class FileTest extends PHPUnit_Framework_TestCase {

    public function testReadErrorThrowsReadException() {
        $handler = fopen(__FILE__, 'r');

        $this->setExpectedException('App\ReadException');

        App\read($handler);
    }

}

上面的测试应该通过了。

现在,如果您无权访问命名空间,我认为您必须重写读取函数以使用“读取器”对象。像这样的:

function read($stream, FileReader $reader) {
    $line = $reader->fgets($stream);

    if (feof($stream)) {
        throw EofException('...');
    }

    if ($line === false) {
        throw new ReadException('Stream error!');
    }

    return $line;
}

interface FileReader {

    public function fgets($handle, $length = 1024);
}

那么测试将是:

class FileTest extends PHPUnit_Framework_TestCase {

    public function testReadErrorThrowsReadException() {
        $handler = fopen(__FILE__, 'r');

        $readerMock = $this->getMock('FileReader');
        $readerMock->expects($this->once())
                ->method('fgets')
                ->with($handler)
                ->will($this->returnValue(false));

        $this->setExpectedException('ReadException');

        read($handler, $readerMock);
    }

}

第二个测试也应该通过。

【讨论】:

  • 感谢详细的示例。我知道总是可以重构源代码并让其他东西处理它,但这并不是一个真正的可扩展解决方案。我不想最终重载每个 PHP 方法,我只是想知道我是否可以让内置返回 false ;)。我最终找到了一个非常简单的方法,所以很遗憾我不得不接受我自己的答案。谢谢你这么详细!
【解决方案2】:

我找到了一个非常简单的方法:

$h = fopen(__FILE__,'a');
read($h);

通过提供只写的流,我们可以轻松地让fgets 返回 false 并触发异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多