DEV Community

Daniel Werner
Daniel Werner

Posted on • Originally published at 42coders.com on

How to use reflection to test private and protected methods

Developing application with TDD methodology we tend to achieve the highest possible code coverage, including private and protected methods as well. But writing unit test for a non public method is not trivial. Changing the visibility for the sake of unit tests is not a good idea. In these cases we can use php’s reflection to make those methods accessible, let’s see how.

Methods

Create new Reflection class from our class:

$class = new \ReflectionClass('MyNamespace\MyClass');
Enter fullscreen mode Exit fullscreen mode

Get the private/protected method:

$myProtectedMethod = $class->getMethod('myProtectedMethod');
Enter fullscreen mode Exit fullscreen mode

Make it accessible:

$myProtectedMethod->setAccessible(true);
Enter fullscreen mode Exit fullscreen mode

Create and instance from our class:

$myInstance = new MyClass();
Enter fullscreen mode Exit fullscreen mode

Call the method with arguments on the instance created above:

$result = $myProtectedMethod->invokeArgs($myInstance, [$argument1, $argument2]);
Enter fullscreen mode Exit fullscreen mode

Properties

In similar way we can we can change the visibility of the non public properties of a class. This could be useful when we want to unit test edge cases or error handling, and we want to change the internal state of the instance to simulate the erroneous behaviour.

Create new Reflection class from our class:

$class = new \ReflectionClass('MyNamespace\MyClass');
Enter fullscreen mode Exit fullscreen mode

Get the private/protected property:

$myProtectedProperty = $class->getProperty(myProtectedProperty);
Enter fullscreen mode Exit fullscreen mode

Make it accessible:

$myProtectedProperty->setAccessible(true);
Enter fullscreen mode Exit fullscreen mode

Create and instance from our class:

$myInstance = new MyClass();
Enter fullscreen mode Exit fullscreen mode

Set the value of the property on the created instance:

$myProtectedProperty->setValue($myInstance, 'value');
Enter fullscreen mode Exit fullscreen mode

You can find more information about the reflection in the php documentation: https://www.php.net/manual/en/book.reflection.php

The post How to use reflection to test private and protected methods appeared first on 42 Coders.

Top comments (7)

Collapse
 
curtisfenner profile image
Curtis Fenner

You generally shouldn't test private members directly.

You should be testing that your implementation meets it's interface by writing tests that use your code as normal.

This is what TDD means when it says that "testable" code is well designed code. If you find yourself needing reflection in order to write tests, your code is not testable (and thus possibly needs better design)

If using your code requires inkoking t private members, they shouldn't be private. If you don't need to invoke private members to use your module, then neither should your tests.

For example, via inversion of control / dependency injection, you can guide the unit under test into whatever state you want by passing "fake" implementations of its dependencies which, for example, trigger error conditions. This also leads to production code that has more separated responsibilities that is easier to refactor (or delete!) since
your code does fewer things in each place.

Collapse
 
daniel_werner profile image
Daniel Werner

This is a highly opinion-based topic, if you should or shouldn't test private/protected methods, just see the discussion here: stackoverflow.com/questions/105007...

I agree with the concepts you mentioned, but in some cases, especially when working with legacy code, creating unit tests for those methods could be beneficial. Also the tests can ensure that you don't make breaking changes in private methods.

Collapse
 
antoniocs profile image
AntonioCS

The stackoverflow link you posted shows that the winner of the discussion (marked as accepted and higher votes) is the answer that tells you NOT to test private methods.

If a private method is changed, the tests for the public methods should pick it up, via a difference in the expected output of the public methods that use the private method.

Collapse
 
nas profile image
Emil

Sometimes there are circumstances that require you to use reflections. Maybe some piece of legacy code that can't be changed right now (because of reasons) but will be changed in the near future.

Generally speaking though;

If you feel like you want to test private/protected methods it is usually a clear indication that the class you are testing "does too much".

Maybe you have some class that internally tokenizes a string. Something like that would be nice to test in isolation but you can't because the method that does the tokenizing is declared as private.

A better way than using reflections to change the accessiblity of the method would be to extract the tokenization code into its own class, a Tokenizer. The main class can now use the tokenizer to tokenize it's strings.

The new Tokenizer class will have public methods and can have it's own unit tests.

Collapse
 
charles_roth_8c0df94d211a profile image
Charles Roth

YES, protected or private methods/functions should be tested SOMEHOW.

I have 50 years of development experience. I was a senior software architect for the world's largest academic search engine for ~10 years. I corralled/nagged/rewarded ~20 developers into raising the unit-test coverage of our million LOC platform from 40% to 75%.

Get over the public/protected/private fetish. Yes, fetish. That's what it is. The limited concepts of privacy/visibility of most programming languages do not come anywhere NEAR the levels of abstraction that we REALLY NEED. (A few languages have concepts like "friend of", which is a start.)

WORKING, TESTED code beats privacy fetishes. Exactly how you go about that varies: different projects, with different staffing #s, different longevities, etc. will give you a different "sweet spot" on the most effective balance of testing vs privacy. That's exactly how it should be. But stark statements like "you should only test the public interface" are LUDICROUS and only apply to theoretical or toy projects.

Early in my career, I built robots that examined jet aircraft turbine blades for pits and cracks. Jet engines are "private" to the aircraft: we certainly don't let passengers touch them. But we don't test them by running the ENTIRE AIRCRAFT! We take them out, and put them in a test frame, and suddenly the engine is "public" to the test frame.

Insisting on this closed mind-set of 3 levels of privacy, is like telling an Inuit that there are only 3 kinds of snow. They'll laugh at you as you fall thru the ice and get eaten by a polar bear. :-)

Collapse
 
andrewmclagan profile image
Andrew McLagan

Whatever you do... disregard this article. You should NEVER in no circumstances ... any ... never .. don't .. ever:

NEVER test private or protected methods. This is not a controversial topic. You are testing implementation details, those can and will change. Unit testing tests the public API of units of software.

FFS... articles like this are so so so dangerous when they posit such things are correct.

Collapse
 
jiteshdhamaniya profile image
Jitesh Dhamaniya

Nice!