Test object with filesystem functions
I'm writing 开发者_如何学编程some objects which make extensive use of the filesystem. I'm not sure what is the right way to test them.
I know in theory I should abstract filesystem functionality in some objects and then mock them, but it would be quite pointless in my case: the main use of the classes I want to test is to manage files. So I would have the same problem when testing the new objects, just shifted one level.
The only way I can think to make the tests is to actually work with the filesystem. The problem is that tests will be ran both in the browser and on the command line, and so I need to work in a directory with write access by everyone. Moreover, this does not seem to be a very portable solution.
Any ideas?
You can mock the filesystem with vfsStream as suggested in the PHPUnit Manual:
vfsStream is a stream wrapper for a virtual file system that may be helpful in unit tests to mock the real file system […] If the PEAR installer is available on your system, you only need to execute the following commands:
$ pear channel-discover pear.bovigo.org $ pear install bovigo/vfsStream-beta
There is examples at Github and also a Wiki
Introduce very low level php functions that do nothing other than call file system functions, and use these within your code.
Then, in your test harness, override this functions with stubs (that possibly record that they've been called). That way you can verify that your code will, in the live system, call the correct low level PHP functions. For a unit test thats all you can do, really.
As I see it you have three options:
- Accept that some tests are better off as Integration Tests. Start the test by creating a little world for the test to run in (in %TEMP%, based on the class/method name) then destroy it when you are done.
- Examine you layer of abstraction, and extract the file IO from the class. The create a interface for this. The remainder use integration tests (but this will be very small). This differs from above in that instead of doing file.Read you write the intent, say ioThingie.loadSettings().
- Mock the IO (as suggested by Gordon above).
I normally go with a mix of options one and two and only use three for edge cases as I find in C# mocking the raw IO layer (System.IO.File) really only shows you can write mocking statements, and makes your test a little too dependent on the implementation, however the vfs above looks like it mocks the FS, rather than a the objects.
Another variant is to mock PHP functions that use file system with runkit. Not sure it is the best solution but it works. There is phpunit-mockfunction - PHPUnit extension that simplifies mocking PHP functions.
So example is:
class Tests extends PHPUnit_Framework_TestCase {
// ... setUp, tearDown, other tests...
function test_1(){
$file_size_to_return = 10;
$fake_filesize = new PHPUnit_Extensions_MockFunction('filesize', $this->obj->tested_method);
$fake_filesize->expects($this->once())->will($this->returnValue($file_size_to_return));
$this->obj->tested_method(); // actually run method we want test that contains filesize() function...
}
}
精彩评论