Testing Protected Methods with PHPUnit

Alright, I know that idea of ‘testing protected methods’ makes some people cringe. That instead of directly testing your protected methods you should test them indirectly via the public methods. I can support that argument, after all, as long as the code is being tested we’re doing our jobs. BUT when tests start to fail where’s the break? is it the public method? or the protected method? maybe you’ve royally fucked something during a refactor and you broke them both … who knows? This is why I personally think that directly testing your protected methods is a good thing, and, since I’m the one writing this post, it’s the right thing to do as well.

Let’s start with an example, let’s say you’re working on this amazing new framework and it’s going to change the world and win you thousands of twitter followers. However, before that happens you need to make sure that you’ve got decent coverage and everything appears to work. Part of your framework deals with persisting uploaded files and since this is the most amazing and flexible framework evar, you’re planning on giving multiple options for persistent storage. Users of the framework can choose to persist their files to the local filesystem (boooring), they can store them in some sort of webscale document store (like /dev/null maybe) or they can even choose to put them up in the cloud because clouds are fluffy and great and that’s where the Care Bears live.

I’m an OO programmer and to me, this use case absolutely begs me to use an abstract base class for the plain jane logic and then a set of subclasses to deal with specifics. Let’s move ahead then and start writing some code. Our base class is going to have a public method called persist, if everything goes as planned, the persist method will move the uploaded file from it’s temporary place on the filesystem to it’s more permanent home on the filesystem/in the document store/in the cloud/where ever. Also, because we’re good developers, the method is also responsible for logging the success or failure of this method.

Side Note: this is probably a good time to mention that all the examples here are available from our GitHub  blog-post-examples repository

Here is the persist method from the abstract base class Persister

and here are the nitty gritty details of the _persist method in the Persister subclass File

Great, we’ve got some code, time to test it. I’m going to focus on showing how to test the protected _persist method of the File subclass but remember since we’re directly testing the protected method we’ve also got to test it’s caller separately. Those tests have been written as well and be seen here if you don’t believe me. Onto the testing …

Method 1: Reflection

PHP’s Reflection API is really handy for doing things your mother told you not to. If you haven’t mucked around with it I really recommend doing so, you can reach into objects and fiddle with their private bits and, as in this example, make protected methods a little more accessible.

Method 2: A ‘Testable’ Subclass

If reflection isn’t your thing you can always define a subclass of the Class Under Test (CUT) which has public wrapper methods of the protected methods you want to test. These classes are typically defined at the bottom of the CUT’s test case file, here is the definition of the TestableFile subclass.

With the TestableFile subclass defined, testing the protected _persist method is really simple.

Method 3: Indirection

Earlier I mentioned I could support the argument that you should test your protected methods indirectly by testing it’s callers from the public api … so here goes.

If you plan on testing your protected methods indirectly via the public api you need to stay focused on the purpose *and expected outcome* of each test. This method doesn’t mean you get to skip testing the public api separately. You should have a set of tests that cover the public caller of the protected method (in our case it’s the persist method of the abstract base class Persister). You can accomplish this by using Mock objects and stubbing the protected method call. This way your tests focus directly on the public method and it’s effects, not the effects of the protected method it calls out to. Similarly, when testing the protected method you only need to focus on the effects of the protected method itself.

That’s it! Testing protected methods has never been easier. Remember, in the end it doesn’t really matter which method you choose. Whether it’s Reflection, a Testable Subclass, or Indirection, your code is being throughly tested and your app is better off because of it. You should feel good!