A question that has befuddled many developers, including myself for a long time, is: how do I test private methods? Should I test private methods?

My opinion is yes, but not directly. I test the behavior of a class’s private methods indirectly through the class’s public methods. In other words, I test my private methods the exact same way my private methods are used by the rest of the application.

The reasoning with my testing approach has to do with the reason why private methods exist. In my mind the value of private methods is that since a private method is hidden from the outside world I can feel free to refactor the private methods at any time and in any way, knowing that I’m not going to mess up any of the code that uses my class.

Here’s a concrete example of a class makes what I think is appropriate use of private methods:

class TypeaheadTag < ActionView::Helpers::Tags::Base def render hidden_field + text_field_tag end private def hidden_field @template_object.hidden_field( @object_name, "#{@method_name}_id", class: "#{@method_name}_id" ) end def text_field_tag @template_object.text_field_tag( @method_name, value, class: "#{@options[:class]} #{@method_name}_typeahead" ) end end

I wrote this class to DRY up Twitter Bootstrap typeahead components, which appeared in many places in an application I was developing. The existence of this class (along with other supporting code) allows me to spit out a typeahead field as succinctly as this:

<%= form.typeahead :person, class: 'form-control' %>

The exact manner in which this typeahead class conjures up its necessary components – a hidden field and a text field – is its own private business. No external party needs to know or care how these things happen. The class should be free to refactor these methods, split these two methods into several methods if desired, or combine them all into one, all without altering its public interface one bit.

This principle is why I think it’s useful for tests not to have any knowledge of a class’s private methods. Again, it’s not that the behavior inside the private methods doesn’t get tested – it does. It just gets tested via the class’s public interface.