How to test private methods?

le 18/07/2011 par Jonathan Scher
Tags: Product & Design

This question is always flying around when you start with Test Driven Development (TDD). It can come from two different sources : either we didn’t followed it “by the book” – and it punished us, or we are trying to use TDD on an existing code. For both cases, you will have to rethink your design.

Emergent Design

You are doing TDD by the book. You add a test: it fails. You make a crappy copy/paste, and it passes. Now you refactor : you create a private method, and you make two calls to it. If you are following TDD’s rules, that’s almost the only case when you will have to face private methods. And those methods will be tested from your two public methods. Actually, with TDD, you write tests before any methods. Therefore, you will never have to ask yourself “how will I test this method?”

If you are doing TDD by the book, and you still ask this question, you may be over-concepting. You may have start to code the core, without knowing how you will integrate it. And you will have surprises when you’ll do it. Delete your code, write a small test, and make it pass. Or read the next paragraph.

I have an untested private method in my code. Now what?

Shit happens. And you have to deal with it every day. Depending on your design, there are three ways to deal with it : make it public – or expose it as a service in a different class, test it through another public method, or make it protected (or package only).

If your private method is actually a service, that could be reused, why not making it public ? That is always an option. You may want to extract it in a different class, in order to be more compliant with the single responsibility principle.

If it does not, why do you want to test it ? Because it’s used by another method of this class? Treat it as if it was part of the code of this other method: you will be able to test it that way. Write a test for your public method: it will test the private method. And coverage tools will understand it.

Sometimes, you have a bug located in this private method. And you don’t want to recreate the whole context of the public method. Consider the first option: could it be extracted as a public service? Maybe in another class? If not, you always can change the visibility to package only. Your method is now accessible by the whole package. So you can write a test class that can access it.

There are other possibilities. If you’re in Java, you can access the method through reflection. In ruby, you can use ‘send’. But those are more advanced stuff, that should be used with caution.

Asking this question should raise an alarm. It shows you that you may have a service instead of a private method. If not, it should be tested as if it was the body of you public method using it.