Accessing private properties in Unit Tests

| | Comments (10) | TrackBacks (0)

While it's absolutly correct that direct access to private properties is strictly forbidden in PHP, it's quite disturbing that even reflection cannot do it when you're writing Unit Tests. Sometimes you just want to test whether a private property contains the correct data. With the normal visibility rules you cannot access those from your Unit Tests.

Luckily, as I just read at Tobias Schlitt's Blog, Derick Rethans committed a patch to the PHP 5.3 repository, which allows you to get things done quite easily. You still need to explicitly state that you want to access the value of a protected/private property through reflection by the new method setAccessible(). It's a good thing to avoid people doing stupid things accidentally, but you finally get the access to it.

PHP 5.3 you'll be able to access private properties like this:

But until we'll see a stable PHP 5.3, there we're many working "hacks", which worked for some versions of PHP 5.x - the most current one would be this:

This should get you up and running for PHP 5.2.5 ("older hacks" won't work).

So how could you use it?

I hope those lines of code will help you until PHP 5.3 arrives, as it helps our developers.

Update

As Sebastian points out in his comment to this post, PHPUnit has had support for testing private and protected attributes for quite a while. It can be used via:

I should be reading the documentation much more thoroughly next time (even though I must admit, that none of our developers has known this method, too).

0 TrackBacks

Listed below are links to blogs that reference this entry: Accessing private properties in Unit Tests.

TrackBack URL for this entry: http://www.maxhorvath.com/blog/mt-tb.cgi/3

10 Comments

PHPUnit has had support for testing private and protected attributes for quite a while via the readAttribute($classOrObject, $attributeName) method which returns the value of a given (static) attribute ($attributeName) of a class or of an object.

Damn, I should have known! :)

Anyway, thanks for pointing it out ... I'll let our developers know, on how to solve problems with private/protected properties in the future.

BTW: Maybe you should consider highlighting this "feature" a little bit more in PHPUnit's documentation, as nobody in our company has noticed this section.

Isn't it a typical example for code smelling if a tests needs to access private/protected members for thorough testing?

> Sometimes you just want to test whether a
> private property contains the correct data.

Unit tests should test the public interface and nothing else. The beauty of test-first is that you don't get bogged down in implementation details while you specify behaviours. It might be convenient to peek under the bonnet briefly to debug but you really don't want any of that visible in the tests.

Noel

@Lars:
Yes, it is. What about this one:

<?php

require_once('PHPUnit/Framework.php');

class A
{
  private $a;

  public function setA($a)
  {
    $this->a = $a;
  }
}

class B extends A
{
  private $b = 'abc';
}

class TestTest extends PHPUnit_Framework_TestCase
{
  /**
   * Tests the setA() method on an instance of A.
   *
   * This test would not be possible, if class A was declared abstract.
   *
   */
  public function testASetA()
  {
    $a = new A();
    $a->setA('xyz');

    $this->assertAttributeEquals('xyz', 'a', $a);
  }

  /**
   * Tests the setA() method on an instance of B.
   *
   * This test ends with a ReflectionException, thus the setA() method cannot
   * be tested here.
   */
  public function testBSetA()
  {
    $a = new B();
    $a->setA('xyz');

    $this->assertAttributeEquals('xyz', 'a', $a);
  }
}

?>

@Noel:
In theory it sounds like you never should need the capability.

But in the real world it is not the case. For example this is a problem, if the parent class of a class is declared abstract, and therefore it cannot be instantiated. One of the most common use cases for this problem ...

@Max: I'm afraid I must respectfully and vigorously disagree :)

Tests are only interested in objects and interfaces. They shouldn't be aware of anything non-public, including abstract classes and their properties. That's an implementation detail. If I want to test-kiss my girlfriend, I don't need to know about her ancestry. Frankly, at moments like these I'd rather not think about her grandmother at all, however fine a lady she may be.

Whenever I see "private" my first thought is to wonder about dumping inheritance in favour of aggregation/composition. A need for "private" bits suggests to me an over-large inheritance hierarchy which ought to be broken up (private has no real purpose except as an enabler of over-large inheritance hierarchies...).

If there is an abstract class, inherited behaviours might be tested just once, perhaps using a derived class written solely for the test. Or perhaps you could find a way to pass multiple objects (an instance of each of the derived classes) past the same set of tests asserting the inherited, core logic (and with further test cases for the specific behaviours added by each derived class). However it's done, tests should exercise the public interface and make assertions based on changes in (public) state or behaviour. If you feel the need to assert non-public state, something is wrong.

With one exception... if there are mocks in the test, some implementation details inevitably are exposed (the interfaces of neighbouring objects). That, however, is the whole point: mocks are a top-down design tool. Writing the test of class A allows you to "discover" all the objects (the mocks) with which it needs to interact - and so on and so on when you come to implement the mocks.

One of the big benefits of test-first is that it concentrates the mind on behaviours and interfaces. This can lead to better-designed classes. At the testing stage, you're only thinking how to describe a behaviour using the language of the domain and the public interface. You don't want to get bogged down in implementation details (which probably won't survive subsequent refactorings in any case). Encapsulation is the name of the game.

Noel, again I agree and disagree ... writing all code by yourself should enable you to work this way ... but having to deal with code by 3rd-parties can change the situation ...

I tried the code only to get a reflection exception being thrown. Further inspection of the manual uncovered the following:

"Note: Trying to get or set private or protected class property's values will result in an exception being thrown."

So I'm not quite sure what versions the above hack was working on (I'm running PHP 5.2.5)

Please use readAttribute($classOrObject, $attributeName) ... it works no matter which version of PHP you are using ...

Leave a comment

Recent Activity

Monday

  • I tweeted, "Just published a new blog post: How to make UILabel / UITableViewCell to have a dynamic height ... http://tinyurl.com/8s55nb"
Wednesday

  • I tweeted, "Flat is prepared for the party ..."
Tuesday

  • I tweeted, "Good morning Twitterverse!"

More ...

Conferences

Conferences I presented at:

Programming Blogs - BlogCatalog Blog Directory
Creative Commons License
This weblog is licensed under a Creative Commons License. blogoscoop

About this Entry

This page contains a single entry by Max Horvath published on March 6, 2008 5:24 PM.

Motivation for Testing was the previous entry in this blog.

Syntax Highlighting for your Movable Type blog is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.