To Test or Not To Test: Unit Testing and Privacy

Determining which methods in your application needed testing used to be easy - test everything exposed by the public API. But once you invite other developers to contribute, you are exposing a whole other set of internal APIs to the team. The behavioral consistency of these methods is just as important as that of the public API, so shouldn't you be testing them too? I would argue you should, even if this internal API consists of private and protected methods. To make life easier, I'll give you a couple of tools for testing these limited-visibility functions without forcing everything to be declared "public."

Over the Thanksgiving holiday, I had an interesting discussion with some of my Twitter peers about unit testing in WordPress:

https://twitter.com/ryancduff/status/406239275194523648

If you know me, you know I like unit testing – even when singletons are involved.  Test-driven development makes life a whole lot easier for developers and QA teams alike, and I will always be a strong advocate of the practice as a standard in web development.  That said, the question of whether or not to test certain parts of an application remains a good one on which there is no definitive answer.

Not To Test

The easiest stance to take is one I often advocate in unit testing overviews or quick tutorials: only test public APIs.

When you’re building an extensible system, testing the API exposed to outside developers is a must.  This suite of tests will help you, as the original developer, verify that the API behaves as expected and exposes the functionality your documentation[ref]You are documenting your API, aren’t you?[/ref] claims it does.

As you further develop your application, you can re-run the tests as often as needed to verify functionality remains intact.  This way you aren’t breaking any promises your API design is making to the community.

If a method is declared as protected – or even private – it establishes the method as an internal behaviour and should not be subject to tests.  Internal behavior – the inner workings and implementation of your system – should remain a black-box to anyone coding against your API.  You can change it as often as you need, so long as the unit tests for your public API continue to pass.

In short – if it’s public, test it.  If it’s private, it’s magic and can be treated as such.

To Test

The alternative argument, which is one I’m beginning to embrace, is that everything should be tested.  Yes, your code exposes an API to the world against which other developers can write code.  The functionality of that API can and should be tested as completely as possible.

But unless you’re a solo developer[ref]Few developers are truly solo acts anymore.  Even if you’re not a member of a larger corporation or entity, I can all but guarantee you will have someone else either contributing to or hacking on your code in the future.[/ref], there’s a second API in your codebase.  Every application, whether it exposes a public interface for developers or not, contains an internal API.  This is the API you code against every day in your project, and it’s just as important as – if not more than – your public API.

Once you invite another developer to work on your project, you’re opening even private and protected methods within your objects to the team.  A method for verifying nonces, for example, is something you would not likely ever expose via a public API.  However, it contains certain functionality that must exhibit a specific, consistent behavior in order for your application to function properly.

In short – if it’s critical to consistent functionality, even private functionality, test it.  There is no such thing as magic.

What is the Question?

There is no generalization I or anyone else can make regarding what code should be tested.  The best we can do is offer guidance to help determine which methods should be tested and which should be considered untestable.

Generally, all public functionality – the exposed API – should be tested.  There might be pieces of functionality that dictate layout or element positioning; these pieces don’t need unit tests but should instead be covered by integration tests.[ref]Integration testing can also be automated, but requires a slightly different toolset than unit testing.  It’s a bit beyond the scope of this article, but definitely something you should look into.[/ref]

Generally, private functionality is internal implementation and won’t need to be tested.  But if the private functionality is used in a utility context (i.e. it’s used by your code and the code written by the guy in the next cubicle) then it absolutely needs to be tested.

I use the term “generally” above in both circumstances to try to explain a best practice.  These are in no ways catch-all rules, however, and your best judgement is required when making the decision whether to or not to test a piece of code.  I would always encourage erring on the side of testing.  You can never have too many tests.

So let’s say you decide there’s some protected or private functionality in your class that needs testing.  How exactly would you go about that?[ref]I work in the world of WordPress, and WordPress lives in the universe of PHP.  All of the code to follow is PHP, but you can probably abstract the concepts to your language of choice.[/ref]

Testing Protected Methods in PHP

If methods are protected, one of the easiest ways to test them is to subclass your object and override them with a public method.  This is the approach I use to unit test my PHP singleton pattern, and it works wonders.

Testing Private Methods in PHP

Private methods are a bit trickier.  Subclassing won’t provide access since private methods aren’t inherited by child classes.  Instead, we can use a neat feature of PHP called reflection.  Reflection will allow our objects to look at their own definitions and manipulate the privacy of their member functions, making things accessible to our tests.

There are more things in heaven and earth, Horatio

Unit testing is a huge deal if you want to maintain clean, bug-free, future-proof code.  It’s a fantastic way to document your API to ensure functionality behaves as expected farther down the road when client requirements change and new code enters the project.  It’s also an effective way to ensure internal consistency between privileged APIs used by your development team.

There is no set of rules that can be universally applied to all code when determining whether or not to test.  When you look at a function and ask, “should I test this?” the answer is probably yes, even if it’s not part of your public API.