PHPUnit测试fopen / fwrite链 - php

在一个项目中,我发现了以下代码行:

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并为两者都使用一个例外?

还是最好的选择是将此方法分为两种-一种用于打开,一种用于书写-分别测试?

参考方案

达到目标的最好方法是使用模拟文件系统。我建议使用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()
        );
    }
}

我希望我能帮忙...

Laravel Lmutator $ this-> attributes返回'Undefined index:id' - php

因此,我正在尝试向模型添加一个属性(评级)。到目前为止,我做到了: public function getRatingAttribute() { return $this::join('reviews', 'accounts.id', '=' , 'reviews.account_id&#…

PHP:对数组排序 - php

请如何排序以下数组Array ( 'ben' => 1.0, 'ken' => 2.0, 'sam' => 1.5 ) 至Array ( 'ken' => 2.0, 'sam' => 1.5, 'ben' =&…

php Singleton类实例将在多个会话中保留吗? - php

举一个简单的例子,如果我想计算一个不使用磁盘存储的脚本的命中次数,我可以使用静态类成员来执行此操作吗?用户1:<?php $test = Example::singleton(); $test->visits++; ?> 用户2:<?php $test = Example::singleton(); $test->visits+…

CakePHP将数据传递到元素 - php

我的控制器中有以下代码:function index() { $posts = $this->set('posts', $this->Portfolio->find('all')); if (isset($this->params['requested'])) { retur…

PHP PDO组按列名称查询结果 - php

以下PDO查询返回以下结果:$db = new PDO('....'); $sth = $db->prepare('SELECT ...'); 结果如下: name curso ABC stack CDE stack FGH stack IJK stack LMN overflow OPQ overflow RS…