As well as testing your own code, you can also test php itself. It's a good way to make some notes about a relatively obscure piece of behaviour which isn't immediately obvious and you can add to this at any time if further issues come to mind.
A trivial example would be (using SimpleTest):
Code: Select all
class TestOfInArray extends UnitTestCase{ function TestOfInArray() { $this->UnitTestCase(); } function test() { $array = array('foo'); $this->assertIdentical(in_array('foo',$array), true); $this->assertIdentical(in_array('bar',$array), false); }} It's a big change in perspective but a really nice way to work. By deciding requirements first and then coding to these you stay focussed. If, for example, you just want to display a "happy birthday" message for site visitors, that's all you do. You don't go off on a mission to create a comprehensive calendar system which can be used for personal organisers, project planning, or whatever else you might want to do involving times and dates. These could be useful classes to have in your library but that can wait.
If you use unit tests to document the behaviour of native php functions, you're turning the test-first-then-code idea on its head. Rather than the code following the tests, the tests follow the code. In the above test you can't edit php to make the test work, you have to alter the assertions until they pass. That's emphatically *not* the way you would normally use a testing framework.
With that out of the way, here's a more useful example of using unit tests to document php. It stems from a "DeleteBranch" class I was working on. It's intended to be a convenient way to delete an entire filesystem branch with a single command, mimicing the behaviour of the unix rm -rf command. There were a number of issues which I had to investigate: does php treat unix symbolic links and windows shortcuts in the same way? Does the dir() pseudo object follow symlinks/shorcuts?
Time to test. First, you need to make the following filesystem branch in Windows and unix. In the test case, set values for $this->_test_root for each OS. The branch contains some files, dirs and a couple of links: one to a file (b_shortcut.txt) and one to a dir (bar_shorcut). Obviously these should be symlinks on unix and shorcuts on windows.
Code: Select all
test root | | foo a.txt b_shortcut.txt[.lnk] bar_shorcut[.lnk] | | bar | | b.txt Code: Select all
class TestOfSymLinks extends UnitTestCase{ function TestOfSymLinks() { $this->UnitTestCase(); } function setUp() { if($this->_isWin()) { $this->_test_root = 'e:\\for_symlinks_tests\\'; # your own settings here } elseif($this->_isNix()) { $this->_test_root = '/home/user/php/for_symlinks_tests/'; } else { trigger_error('OS not recognised - please add to list.'); } } function testIsLink() { if($this->_isWin()) { $this->assertIdentical( is_link($this->_test_root . 'bar_shortcut.lnk'), false); #!! $this->assertIdentical( is_link($this->_test_root . 'b_shortcut.txt.lnk'), false); #!! } elseif($this->_isNix()) { $this->assertIdentical( is_link($this->_test_root . 'bar_shortcut'), true); $this->assertIdentical( is_link($this->_test_root . 'b_shortcut.txt'), true); } } function testIsFile() { if($this->_isWin()) { $this->assertIdentical( is_file($this->_test_root . 'a.txt'), true); $this->assertIdentical( is_file($this->_test_root . 'b_shortcut.txt.lnk'), true); } elseif($this->_isNix()) { $this->assertIdentical( is_file($this->_test_root . 'a.txt'), true); $this->assertIdentical( is_file($this->_test_root . 'b_shortcut.txt'), true); } } function testIsDir() { if($this->_isWin()) { $this->assertIdentical( is_dir($this->_test_root . 'foo'), true); $this->assertIdentical( is_dir($this->_test_root . 'bar_shortcut.lnk'), false); #!! } elseif($this->_isNix()) { $this->assertIdentical( is_dir($this->_test_root . 'foo'), true); $this->assertIdentical( is_dir($this->_test_root . 'bar_shortcut'), true); } } function testFileType() { if($this->_isWin()) { $this->assertEqual( filetype($this->_test_root . 'foo'), 'dir'); $this->assertIdentical( filetype($this->_test_root . 'bar_shortcut.lnk'), 'file'); #!! $this->assertEqual( filetype($this->_test_root . 'a.txt'), 'file'); $this->assertIdentical( filetype($this->_test_root . 'b_shortcut.txt.lnk'), 'file'); #!! } elseif($this->_isNix()) { $this->assertEqual( filetype($this->_test_root . 'foo'), 'dir'); $this->assertEqual( filetype($this->_test_root . 'bar_shortcut'), 'link'); $this->assertEqual( filetype($this->_test_root . 'a.txt'), 'file'); $this->assertEqual( filetype($this->_test_root . 'b_shortcut.txt'), 'link'); } } function testCanonicalExpansion() { if($this->_isWin()) { $this->assertNotEqual( realpath($this->_test_root . 'b_shortcut.txt.lnk'), $this->_test_root . 'foo/bar/b.txt'); #!! } elseif($this->_isNix()) { $this->assertEqual( realpath($this->_test_root . 'b_shortcut.txt'), $this->_test_root . 'foo/bar/b.txt'); } } function testDirPseudoObjectFollowsSymlinkToTarget() { if($this->_isNix()) { $it =& dir($this->_test_root . 'bar_shortcut'); $contents = array(); while(false !== ($item = $it->read())) { $contents[] = $item; } $it->close(); $this->assertIdenticalArrayValues($contents, array('.', '..', 'b.txt')); } } // it's not a valid path without .lnk function testWinShortcutsMinusTheLnkExtension() { if($this->_isWin()) { $this->assertIdentical(is_link($this->_test_root . 'bar_shortcut'), false); $this->assertIdentical(is_link($this->_test_root . 'b_shortcut.txt'), false); $this->assertIdentical(is_file($this->_test_root . 'b_shortcut.txt'), false); $this->assertIdentical(is_dir($this->_test_root . 'bar_shortcut'), false); $this->assertIdentical(filetype($this->_test_root . 'bar_shortcut'), false); $this->assertIdentical(filetype($this->_test_root . 'b_shortcut.txt'), false); $this->assertNotEqual( realpath($this->_test_root . 'b_shortcut.txt'), $this->_test_root . 'foo/bar/b.txt'); } } function _isNix() { return preg_match('/linux/i', $_SERVER['SERVER_SOFTWARE']); } function _isWin() { return preg_match('/win/i', $_SERVER['SERVER_SOFTWARE']); }} In Windows you can see that:
(1) realpath cannot expand windows shortcuts
(2) shortcuts are never identified as links on windows.
- shortcuts to files have type 'file' not 'link'
- shortcuts to dirs are also type 'file' ie they're not following the shortcut to its target
In unix, the dir() pseudo object will indeed follow symlinks to their target. This was important to know for my DeleteBranch class. This reads through a branch recursively, and so could easily end up deleting much more than you bargained for if it follows links...
On unix, an is_dir($path) check will return true for real dirs and symlinked dirs but if('dir' == filetype($path)) will only return true for dirs. The latter allows you to filter out symlink paths, should you need to.
The test case provides a source of documentation which I can refer to later when I forget the results, as I inevitably will. With a written test in place, I can add to it later if further twists come to mind, building up my knowledge. I can also run this against new versions of php to check if windows behaviour might have been brought into line with unix.
The phpv4.4 "update" caused a lot of problems for OOP programmers. You have to declare a new object as a variable before returning it rather than returning the new object directly. Tests might provide some early warning for this kind of thing. It would be a mammoth task to try to cover all of php with tests of course. If only the developers had written a manual expressed as test cases...