Testing Your Privates

Sebastian Bergmann » 09 February 2010 » in Articles » 13 Comments

This article is part of a series on testing untestable code:

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".

Defined tags for this entry: , ,

Trackback specific URI for this entry

13 Comments to "Testing Your Privates"

Display comments as (Linear | Threaded)
  1. Evert
    09/02/2010 at 14:05 Permalink
    Although I agree with the sentiment, there have definitely been cases where using private methods would have been very useful. Using Reflection hadn't occurred to me at all =)

    Will bring me a bit closer to the magical 100% coverage!

    Reply

  2. Daan Broekhof
    09/02/2010 at 14:26 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 (which is the same as and 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

  3. Daan Broekhof
    12/02/2010 at 10:35 Permalink
    Hmm I should really check my comments after posting; all 'taglike' text and a PHP code example were stripped out, which removes most of the meaning from my post. So I hope this one makes more sense, not sure what the formatting rules are on this blog. ;)
    ------------
    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::&lt;!public&gt; (which is the same as classname::&lt;protected&gt; and classname::&lt;private&gt; 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

  4. Jordan Walker
    09/02/2010 at 15:10 Permalink
    The title is extremely catchy and well understood when having fully read the article. Nice write up!

    Reply

  5. Ben
    09/02/2010 at 15:32 Permalink
    Thanks alot for the post, its handy to know how to test private methods correctly.

    Reply

  6. Test Bastard
    09/02/2010 at 16:10 Permalink
    What's the point? Testing your privates is a waste of time. You should not spend energy on testing this. 100% test coverage does NOT mean that you should test each and every attribute and method.

    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

  7. Sebastian Bergmann
    09/02/2010 at 17:31 Permalink
    I am AGAINST testing private methods and private state. I only posted this entry in an effort to stop people from asking me about this :-)

    Reply

  8. Noel Darlow
    09/02/2010 at 17:26 Permalink
    When you're testing a class you should only test the public interface. What you're really doing is designing a component and its API. Who cares how it works. That's not what you should be thinking about, at least not initially. The first step is to imagine the simplest, most self-explanatory (ie well-named) interface possible. This will be described in a test and then - only then - should you start thinking about implementation details (ie the simplest possible thing which can make the test pass).

    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

  9. Josh Adell
    09/02/2010 at 20:03 Permalink
    Our development team came up with a library for creating dynamic wrappers for testing protected methods. We're stuck on PHP 5.2 for now, so no setAccessible for us.

    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. jpauli
    10/02/2010 at 10:04 Permalink
    You wrote that PHP5.3.2 supports that. Actually PHP 5.3.x supports that.

    Reply

  11. Sebastian Bergmann
    10/02/2010 at 10:06 Permalink
    I implemented ReflectionMethod::setAccessible(). The first release that it will be part of will be PHP 5.3.2.

    The related ReflectionProperty::setAccessible() was introduced in PHP 5.3.0.

    Reply

  12. coding horror
    10/02/2010 at 12:18 Permalink
    `assertAttributeEquals` is less verbose than the whole reflection approach. Could we hide the reflection magic for private methods too?

    Reply

  13. Sebastian Bergmann
    10/02/2010 at 12:26 Permalink
    I do not want to convolute PHPUnit with functionality that makes "bad practice" convenient. I feel dirty already for adding ReflectionMethod::setAccessible() to PHP.

    Reply

5 Trackbacks to "Testing Your Privates"

  1. www.websdeveloper.com 09/02/2010 at 19:01
  2. Sebastian Bergmann 16/02/2010 at 08:20
    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 In a unit test, mock objects can simulate the behavior of complex, real
  3. Sebastian Bergmann 16/02/2010 at 08:20
    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 With PHPUnit 3.5 it will be possible to stub and mock static methods. C
  4. Sebastian Bergmann 16/02/2010 at 08:21
    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 I frequently quote Miško Hevery with "It is hard to test code that uses
  5. Sebastian Bergmann 16/02/2010 at 08:21
    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 hel

Add Comment


To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Submitted comments will be subject to moderation before being displayed.