When dealing with external projects that you either want to re-use parts of or refactor, being able to write unit tests is often a big problem, especially if it’s written in procedural code like;
function aFunctionIWantToTest($foo) { $foo = aFunctionIDontWantToTest($foo); } function aFunctionIDontWantToTest($foo) { global $conf; // aaargh! if ( $conf['reevaluateMeEveryTime'] ) { doSomethingPainful($foo); } else { ohGodAndAnotherFunction($foo); } }
Being able to “mock” aFunctionIDontWantToTest in a similar manner to Mock Objects would be a big help.
Brought this up a while back here on the SimpleTest feature tracker and sent a mail today to the SimpleTest list with an initial implementation.1).
After discovering APD's (at least in the old 0.4 version I was using) rename_function does very wierd things settled on runkit as the tool to help “mock” a function, which so far seems to work fine. Be warned you can rename anything including pre-defined PHP functions - lots to room to shoot yourself in the foot.
Example use (testing this);
class html_hilight_test extends UnitTestCase { function testHighlightTwoWords() { MockFunction::generate('unslash'); $html = 'Foo bar Foo php Foo'; MockFunction::setReturnValueAt(0,'unslash','bar'); MockFunction::setReturnValueAt(1,'unslash','php'); MockFunction::expectCallCount('unslash',4); $this->assertPattern( '/Foo <span.*>bar<\/span> Foo <span.*>php<\/span> Foo/', html_hilight($html,'bar php') ); MockFunction::restore('unslash'); } }
Basically the methods are all the same as normal SimpleTest mock objects but are called statically via the the MockFunction class.
Note: you must call MockFunction::restore() yourself when you’re finished, otherwise you’ll have mock functions “lying around”, which may lead to very hard to debug problems when running other tests
Source code available here: mockfunctions.tar.gz