13

With PHPUnit and PHP >= 5.3 it is possible to test protected methods. The following page at stackoverflow outlined the best practice on it:

"Best practices to test protected methods with PHPUnit"

protected static function callProtectedMethod($name, $classname, $params) {
  $class = new ReflectionClass($classname);
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  $obj = new $classname($params);
  return $method->invokeArgs($obj, $params);
}

To test public methods on abstract classes is easy with PHPUnit. To test protected methods on normal classes is easy with approach above. To test protected methods on abstract classes must be possible somehow...

I know PHPUnit derives abstract classes and "implements" abstract methods in a concrete class and fires the tests against that concrete class - but i do not know how to integrate that into the approach above to have a callProtectedMethodOnAbstractClasses().

How are you doing such tests?

PS: The question is NOT about the truth of testing protected methods (see: white-, gray- and blackbox-testing). The need of testing protected methods depends on your test strategy.

Community
  • 1
  • 1
Daniela Waranie
  • 161
  • 1
  • 1
  • 4

2 Answers2

29

Since you are asking for a 'best practice' I'll take a different approach to answer:

Don't test protected and private methods

Just because you can doesn't mean you should.

You want to test that a class works. That means that all the functions you can call on it (everything public) return the right values (and maybe call the right functions on objects passed in) and nothing else.

You don't care how this is implemented in the class.

Imho it is even hurting you to write test for anything non-public for two big reasons:

  • Time

Writing tests takes longer as you need more and refactoring also takes longer. If you move around code in a class without changing its behavior you shoudn't be required to update its tests. The tests should tell you that everything still works!

  • Meaningful code coverage

If you write a test for every protected method you loose one inherit benefit from the code coverage report: It won't tell you which protected functions aren't call anymore. That is (imho) a bad thing because you either don't test all the public methods correctly (why is there a method that isn't called if you test every case?) or you really don't need that method anymore but since it's "green" you don't give it a second thought.

To quote the PHPUnit Author

So: Just because the testing of protected and private attributes and methods is possible does not mean that this is a "good thing".

http://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html

Since the real world sometimes differs

...->setAccessible() is fine for normal methods

for abstract stuff use ...->getMockForAbstractClass()

But please do so only if it is really necessary.

A protected method in an abstract class will get tested by testing the public api of its children anyway with my arguments from above applying.

Jason Roman
  • 8,146
  • 10
  • 35
  • 40
edorian
  • 38,542
  • 15
  • 125
  • 143
  • Yeah. I now get the point of *not* testing privates. Thanks edorian. – Stephane Gosselin May 25 '11 at 16:35
  • 8
    The whole point of unit testing is not only know if "the class works" but also know where is the problem if it does NOT work. For this purpose testing of private methods seems to be very reasonable to me. – Petr Peller Aug 10 '11 at 11:19
  • 1
    @Petr I don't think the immensely increased maintenance cost and, from I've seen, decreased quality of the test suites are not worth the time it saves you possibly debugging one or two private calls. Maybe if you don't generate code coverage for the tests that directly test private methods (so your real tests are still testing behaviors) it might work out. So far I've never spoken to someone that did testing of private methods in a bigger environment and didn't fail horribly (by ending up with a sort-of-useless test suite that always had to be changed when any minimal refactorings where made) – edorian Aug 10 '11 at 14:29
  • I appreciate @edorian answer but also agree with Petr answer. I have Override protected-method (Extension_Class extends Parent_framework) then I need to test method of Extension_Class. – Manish Trivedi Sep 16 '13 at 06:20
  • 3
    Why I use protected methods. I have classes with very complex and long code and I don't have any reason to split it into different classes. So I split code into different protected methods inside a class. And I am getting following profit: 1) class have only those public methods which should be visible for its clients 2) I can test every step of a large algorithm separately and 3) I can test high-level parts of an algorithm by mocking low-level protected methods wich are used by high-level part. All this leads to more clear, much shorter and much understandable test code. – Alexander Pravdin Dec 21 '14 at 08:33
  • 5
    Protected methods are part of the public contract that your class is providing and they should definitely be tested. "A protected method in an abstract class will get tested by testing the public api of its children anyway with my arguments from above applying." this violates the very foundation of unit testing. You're describing integration testing which is not a replacement for proper unit test coverage and separation of concerns. – Samuel Feb 03 '15 at 17:26
  • If protected methods are only used within inheritance, then they are not part of the public API. Make them from protected to public if you want them to be a unit to test and use getMockForAbstractClass to test the method as a unit (the mock only makes it accessible). That method should only rely parameters passed to it, not `$this`. Otherwise it's still abstract. You test abstractions in the design, not in the implementation. – hakre Jul 15 '15 at 21:11
  • 1
    I understand the "design style" issue at testing protected methods. However, like @PetrPeller and others, there are real life contingencies that don't care about pure design. In example, I have a 300 lines long method (!), refactored into 10, clearly divided blocks or "steps". Since each block is only called once and by the former "mega method", I made them protected methods. I want to unit test each of those clearly divided blocks. I have thousands of tests, adding 10 more is not going to drive me bankrupt or something. Actually, in the end they speed up my deployment by a factor of 3+! – Dario Fumagalli Jan 03 '17 at 18:38
5

Assumption: You want to call concrete protected methods on an abstract class.

Create a mock object for the abstract class, and pass it to this modified form of callProtectedMethod().

public static function callProtectedMethod($object, $method, array $args=array()) {
    $class = new ReflectionClass(get_class($object));
    $method = $class->getMethod($method);
    $method->setAccessible(true);
    return $method->invokeArgs($object, $args);
}

public function testGetArea() {
    $rect = $this->getMockForAbstractClass('RandomRectangle');
    self::callProtectedMethod($rect, 'setWidth', array(7));
    self::callProtectedMethod($rect, 'setHeight', array(3));
    self::assertEquals(21, $rect->getArea());
}

You could encapsulate this into a single method, but I prefer to pass in the object so that a test can call multiple protected/private methods on the same object. To do so, use $class->isAbstract() to decide how to construct the object.

David Harkness
  • 35,992
  • 10
  • 112
  • 134