【问题标题】:PHPUnit Test for chain of fopen/fwrite用于 fopen/fwrite 链的 PHPUnit 测试
【发布时间】:2023-03-17 03:10:01
【问题描述】:

在一个项目中我发现了这样的代码行:

protected function save($content, $path)
{
    // ...
    if (($handler = @fopen($path, 'w')) === false) {
        throw new Exception('...');
    }

    // ...
    if (@fwrite($handler, $content) === false) {
        throw new Exception('...');
    }

    // ...
    @fclose($handler);
}

我想用 PHPUnit 测试这个方法,但我有点坚持正确的测试用例。如果我将通过不正确的$path 或正确的$path,但权限不正确(例如0444),那么一切都会在第一个异常处停止。如果我以正确的权限传递正确的$path,那么 PHP 也将能够写入文件,并且不会遇到第二个异常。

那么有没有什么方法可以在不重写这个方法的情况下测试第二个异常呢?

或者最好在一种情况下同时检查fopenfwrite 并为两者只使用一个例外?

或者最好的选择是将这个方法分成两种——一种用于打开,一种用于编写——并分别测试?

【问题讨论】:

  • 我会使用模拟的 $path,请参阅 phpunit.de/manual/current/en/… - 使用 vfsStream 您可以设置虚拟磁盘配额,这应该(尽管硬件故障)是 fwrite 返回 false 的最常见原因。 vfsStream::setQuota() 是首选方法。
  • @l-x 你的评论应该是一个答案:)
  • 是的,我可以肯定地接受它:) 很好的测试包。
  • 所以我写了一个小答案;)

标签: php exception phpunit


【解决方案1】:

实现目标的最佳方式是使用模拟文件系统。我推荐使用vfsStream:

$ composer require mikey179/vfsStream

首先我必须提到,fread 仅在您使用无效参数调用此函数时才返回 false。如果发生任何其他错误,它将返回已写入的字节数。所以你将不得不添加另一个检查:

class SomeClass {
    public function save($content, $path)
    {
        // ...
        if (($handler = @fopen($path, 'w')) === false) {
            throw new Exception('...');
        }

        $result = @fwrite($handler, $content);

        // ...
        if ($result === false) { // this will only happen when passing invalid arguments to fwrite
            throw new Exception('...');
        }

        // ...
        if ($result < strlen($content)) { // additional check if all bytes could have been written to disk
            throw new Exception('...');
        }

        // ...
        @fclose($handler);
    }
}

该方法的测试用例可能如下所示:

class SomeClassTest extends \PHPUnit_Framework_TestCase {

    /**
     * @var vfsStreamDirectory
     */
    private $fs_mock;

    /**
     * @var vfsStreamFile
     */
    private $file_mock;

    /**
     * @var $sut System under test
     */
    private $sut;

    public function setUp() {
        $this->fs_mock = vfsStream::setup();
        $this->file_mock = new vfsStreamFile('filename.ext');
        $this->fs_mock->addChild($this->file_mock);

        $this->sut = new SomeClass();
    }

    public function testSaveThrowsExceptionOnMissingWritePermissionOnFile() {
        $this->expectException(\Exception::class);

        $this->file_mock->chmod(0);
        $this->sut->save(
            'content',
            $this->file_mock->url()
        );
    }

    public function testSaveThrowsExceptionOnMissingWritePermissionOnDirectory() {
        $this->expectException(\Exception::class);

        $this->fs_mock->chmod(0);
        $this->sut->save(
            'content',
            $this->fs_mock->url().'/new_file.ext'
        );
    }

    public function testSaveThrowsExceptionOnInvalidContentType() {
        $this->expectException(\Exception::class);

        $this->fs_mock->chmod(0);
        $this->sut->save(
            $this,
            $this->file_mock->url()
        );
    }

    public function testSaveThrowsExceptionOnDiskFull() {
        $this->expectException(\Exception::class);

        $this->fs_mock->chmod(0777); // to be sure
        $this->file_mock->chmod(0777); // to be sure

        vfsStream::setQuota(1); // set disk quota to 1 byte

        $this->sut->save(
            'content',
            $this->file_mock->url()
        );
    }
}

希望能帮上忙……

【讨论】:

  • 如果我不使用作曲家,我不确定从哪里开始。
  • @Jazzzzzz 从安装作曲家开始 - 你不会回头
猜你喜欢
  • 1970-01-01
  • 2015-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多