Thursday, September 1, 2022

[SOLVED] PHPUnit & Git: How to test with unreadable file?

Issue

Short version: For unit testing, I need an unreadable file to make sure the correct exception is thrown. Obviously, that unreadable file can't be stored by Git, so I chmod 000 at testing time and use git update-index --assume-unchanged so that Git doesn't try storing the unreadable file. But then I can't checkout a different branch, but get the error "Your local changes to the following files would be overwritten by checkout."

Is there a better way to test, OR a better way to use Git so that everything plays nicely?

Long version: In one class, I have a method to read a file so that its contents can be imported into a database:

public function readFile($path) {
    ...
    if(!is_readable($path))
      throw new FileNotReadableException("The file $path is not readable");
    ...
  }

I test that method using PHPUnit, and one test in particular should (indirectly) trigger the FileNotReadableException:

/**
 * @expectedException Data\Exceptions\FileNotReadableException
 */
public function testFileNotReadableException() {
  $file = '/_files/6504/58/6332_unreadable.xlsx';
  @chmod(__DIR__ . $file, 0000);
  $import = Import::find(58);
  $import->importFile(array('ID'=>2, 'newFilename'=> $file), __DIR__);
}

After a test, git checkout other_branch will abort:

error: Your local changes to the following files would be overwritten by checkout:
    Data/Tests/_files/6504/58/6332_unreadable.xlsx
Please, commit your changes or stash them before you can switch branches.
Aborting

Solution

You have a couple of options.

Add a tearDown() method to the test that resets the file permissions so that git does not think that the file was modified. Then even if the test fails the file will be reset.

http://phpunit.de/manual/current/en/fixtures.html

public function tearDown() {
     @chmod(__DIR__ . $file, 0755); //Whatever the old permissions were;
}

If you are using PHP 5.3+, you can use name-spacing and mock the is_readable function. In your test file, override is_readable with your own function. You will need to make sure that your override is in the same namespace as your class that you are testing.

http://www.schmengler-se.de/-php-mocking-built-in-functions-like-time-in-unit-tests

In your class you would do this:

namespace SUT

class SUT {
    public function readFile($path) {
        ...
        if(!is_readable($path))
          throw new FileNotReadableException("The file $path is not readable");
        ...
  }
}

Then in your test you do the following:

namespace SUT

function is_readable($filename) {
    if (str_pos('unreadable') !== FALSE)) {
        return false;
    }

    return true;
}

class SUTTest extends PHPUNIT_Framework_TestCase {
    /**
     * @expectedException Data\Exceptions\FileNotReadableException
     */
     public function testFileNotReadableException() {
        $file = '/_files/6504/58/6332_unreadable.xlsx';
        $sut = new SUT();
        $sut->readFile($file);
     }
}

You would then not even have to include the files in your repo or worry about the permissions on it.



Answered By - Schleis
Answer Checked By - Marie Seifert (WPSolving Admin)