Testing protected Methods in Unit Tests
As a followup to my talks on the IPC 07 in Frankfurt here the improved version including:
- support for parameter in constrcutors ( via reflection )
- small bug fix avoiding autoload to conflict
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /** * Create proxy of given class. Proxy allows to test of protected class methods * @param string $superClassName * @param array|null $constructorParams parameters for contructor * @return object */ function getProxy($superClassName, array $params = null) { $proxyClassName = "{$superClassName}Proxy"; if (!class_exists($proxyClassName, false)) { $class = <<<class> class $proxyClassName extends $superClassName { public function __call($function, $args) { $function = str_replace('protected_', '_', $function); return call_user_func_array(array(&$this, $function), $args); } } CLASS; eval($class); } if (!empty($params)) { // Create an instance using Reflection, because constructor has parameters $class = new ReflectionClass($proxyClassName); $instance = $class->newInstanceArgs($params); } else { $instance = new $proxyClassName(); } return $instance; } </class> |
Example:
1 2 3 | $oClass = getProxy( 'myClass', array( 'some params')); $oClass->protected_myProtectedFcuntion(); ... |
November 16, 2007 | Filed Under PHP related
Comments
13 Responses to “Testing protected Methods in Unit Tests”
Leave a Reply
Grmbl - don’t mind the converted > please - this wordpress editor is somehow a bit strange.
What else you can do is adding
return parent::__call($function, $args)
for calling possibly already existing __call() method in $superClassName.
hi nice and useful piece of code!
I see one small problem that call_user_func_array only returns false on error (method does not exist). This is hard to debug (e.g. phpunit just stops without a message). So I added an exception Additionally I added getter and setters for non public vars which can be useful as well for unit test.
To code inside the proxy class:
public function __call(\$function, \$args)
{
\$function = str_replace(’UNIT’, ‘_’, \$function);
if(method_exists(\$this,\$function)){
return call_user_func_array(array(&\$this, \$function), \$args);
}else{
throw new Exception(’Method ‘.\$function.’ in class ‘.get_class(\$this).’ does not exist’);
}
}
public function setNonPublicVar(\$name, \$value)
{
\$this->\$name = \$value;
}
public function getNonPublicVar(\$name)
{
return \$this->\$name;
}
Really good and really interesting post. I expect (and other readers maybe :)) new useful posts from you!
Good luck and successes in blogging!
Dig the blog a LOT!
Nice style and I like the way you discuss the problems . I’m going to book mark it.
Yes very cool, thanks a lot.
I didn’t understand the reason for the (my ignorance not your fault at all) on line 38 so I just removed it and went with the text book heredoc notation $class =
lt&;lt&;lt&;CLASS and then ended with CLASS (totally left justified of course).
Probably as a result (though not entirely sure) I needed to put in the back slashes in the eval’d code like MaFi showed in his post.
I also modified str_replace so the replace string was an empty string (ie, protected_myToString becomes just myToString).
Then just called getProxy in my PHPUnit setUp() method and added in the protected_ prefix to calls to protected functions and PHPUnit worked well.
Thanks Frontalaufprall and MaFi really handy work.
Apologies: “…that MaFi showed in her/his post..”
“his” Post is correct Dan
Hi guys,
What is the <<<class syntax, and how does it work? I tried to google it, but nothing came up
See this:
http://de.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc
http://de.php.net/manual/en/language.types.string.php#language.types.string.syntax.nowdoc
great! thanks! I wasn’t aware of that syntax :*)
Making PHP non public class/object methods available…
Anyone who did unit testing in PHP knows that private methods are untestable. One workaround for this could be to make no private but protected only members and call them using a proxy class. Another approach I’d like to present here is a small extens…