Testing Your Privates
This article is part of a series on testing untestable code:
- Testing private methods
- Testing code that uses singletons
- Stubbing static methods
- Stubbing hard-coded dependencies
No, not those privates. If you need help with those, this book might help.
One question I get over and over again when talking about Unit Testing is this:
"How do I test the private attributes and methods of my objects?"
Lets assume we have a class Foo:
<?php
class Foo
{
private $bar = 'baz';
public function doSomething()
{
return $this->bar = $this->doSomethingPrivate();
}
private function doSomethingPrivate()
{
return 'blah';
}
}
?>
Before we explore how protected and private attributes and methods can be tested directly, lets have a look at how they can be tested indirectly.
The following test calls the testDoSomething() method which in turn calls the doSomethingPrivate() method:
<?php
class FooTest extends PHPUnit_Framework_TestCase
{
/**
* @covers Foo::doSomething
* @covers Foo::doSomethingPrivate
*/
public function testDoSomething()
{
$foo = new Foo;
$this->assertEquals('blah', $foo->doSomething());
}
}
?>
The test above assumes that testDoSomething() only works correctly when testDoSomethingPrivate() works correctly. This means that we have indirectly tested testDoSomethingPrivate(). The problem with this approach is that when the test fails we do not know directly where the root cause for the failure is. It could be in either testDoSomething() or testDoSomethingPrivate(). This makes the test less valuable.
PHPUnit supports reading protected and private attributes through the PHPUnit_Framework_Assert::readAttribute() method. Convenience wrappers such as PHPUnit_Framework_TestCase::assertAttributeEquals() exist to express assertions on protected and private attributes:
<?php
class FooTest extends PHPUnit_Framework_TestCase
{
public function testPrivateAttribute()
{
$this->assertAttributeEquals(
'baz', /* expected value */
'bar', /* attribute name */
new Foo /* object */
);
}
}
?>
PHP 5.3.2 introduces the ReflectionMethod::setAccessible() method to allow the invocation of protected and private methods through the Reflection API:
<?php
class FooTest extends PHPUnit_Framework_TestCase
{
/**
* @covers Foo::doSomethingPrivate
*/
public function testPrivateMethod()
{
$method = new ReflectionMethod(
'Foo', 'doSomethingPrivate'
);
$method->setAccessible(TRUE);
$this->assertEquals(
'blah', $method->invoke(new Foo)
);
}
}
?>
In the test above we directly test testDoSomethingPrivate(). When it fails we immediately know where to look for the root cause.
I agree with Dave Thomas and Andy Hunt, who write in their book "Pragmatic Unit Testing":
"In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out."
So: Just because the testing of protected and private attributes and methods is possible does not mean that this is a "good thing".
09/02/2010 at 14:05 Permalink
Will bring me a bit closer to the magical 100% coverage!
Reply
09/02/2010 at 14:26 Permalink
This way you can test your public interface without going into your encapsulation details, yet also achieve complete coverage and detect potential uncovered cases in your code.
Reply
12/02/2010 at 10:35 Permalink
------------
Additionally, if you want to test one specific public method, but also want to see any coverage of protected/private methods, you can also include all protected/private methods in the @covers directive via classname::<!public> (which is the same as classname::<protected> and classname::<private> combined).
This way you can test your public interface without going into your encapsulation details, yet also achieve complete coverage and detect potential uncovered cases in your code.
Reply
09/02/2010 at 15:10 Permalink
Reply
09/02/2010 at 15:32 Permalink
Reply
09/02/2010 at 16:10 Permalink
You should code against interfaces, and you should test what is exposed by the interface. It is a total waste of energy and money to test your private methods and attributes (it's like testing getters and setters -- fail!). In any case, if they were to fail; wouldn't the methods that depend on on them fail? If not, there is something terribly wrong with your code.
Reply
09/02/2010 at 17:31 Permalink
Reply
09/02/2010 at 17:26 Permalink
It's a crucial point: don't get bogged down in implementation when you are designing a class interface - and hence don't ever try to test non-public bits. The design will suffer and you'll lose the one-step-at-a-time simplicity of test-first programming which makes writing code so easy.
Reply
09/02/2010 at 20:03 Permalink
It's available at http://code.google.com/p/shunt
Also available there is an article on the wiki explaining our desire to test the non-public interface of a class.
Reply
10/02/2010 at 10:04 Permalink
Reply
10/02/2010 at 10:06 Permalink
The related ReflectionProperty::setAccessible() was introduced in PHP 5.3.0.
Reply
10/02/2010 at 12:18 Permalink
Reply
10/02/2010 at 12:26 Permalink
Reply
01/07/2010 at 11:42 Permalink
i agree with "don't expose your privates!" but if i want to test my class, i must test all methods, otherwise privates and protecteds will not be tested ?
So with 5.3 i modify your mocked_class.tpl.dist and put :
public function __call($methodName, $arguments)
{
$object = new ReflectionObject($this);
if ($object->hasMethod($methodName))
{
$method = $object->getMethod($methodName);
if (!$method->isPublic())
{
$method->setAccessible(true);
return $method->invoke($this,$arguments);
}
}
}
This help me to unit test all my methods.
Thx for your great job
Reply
06/07/2010 at 12:08 Permalink
invokeArgs is working better than invoke :D
Reply