我正在尝试为一个看起来像这样的函数编写一个单元测试:
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);
我正在尝试弄清楚如何在我们没有到达文件末尾的情况下模拟返回 false fgets
。我想为ReadException
情况编写一个单元测试。
这可能吗?
如果您要测试的代码位于命名空间中,则可以使用此技巧:
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('AppReadException');
Appread($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);
}
}
第二个测试也应该通过。
我找到了一种非常简单的方法:
$h = fopen(__FILE__,'a');
read($h);
通过提供仅写入的流,我们可以轻松地fgets
返回 false 并触发异常。