Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit Test for chain of fopen/fwrite

In one project I have found such lines of code:

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

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

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

I would like to test this method with PHPUnit, but I am a little bit stuck with correct test-case. If I will pass incorrect $path or a correct $path with incorrect permissions (e.g. 0444) then everything will stop at the first exception. If I pass correct $path with correct permissions than PHP will also be able to write to the file and the second exception won't be reached.

So is there any way to test the second exception without rewriting this method?

Or it is better to check both fopen and fwrite in one condition and use only one exception for both?

Or the best option is to separate this method into two - one for opening and one for writing - and test them separately?

like image 685
Gino Pane Avatar asked Dec 02 '25 20:12

Gino Pane


1 Answers

The best way to achieve your goal is to use a mocked file system. I recommend to use vfsStream:

$ composer require mikey179/vfsStream

First of all I have to mention, that fread only returns false if you call this function with an invalid argument. If any other error occurs it will return the number of bytes that has been written. So you will have to add another check:

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);
    }
}

The testcase for the method could look like this:

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()
        );
    }
}

I hope I could help...

like image 84
l-x Avatar answered Dec 04 '25 09:12

l-x



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!