Your Unit Tests Should Mind Their Own Business
Private Methods
One of the most common questions people ask is whether to unit test private methods. I struggle to find any reasons why we should, but I do know a lot of reasons why we should not. I'm not saying that there's never a reason to unit test private methods, I'm just saying to make sure you understand what that reason is before you go to all the effort.
In my opinion, the best way to achieve our unit test objectives is by validating our class's interfaces. And when I think about interfaces, I'm thinking about the public methods we expose to other modules in our application. If we can validate that each of these methods is behaving as expected, then I'm going to feel satisfied that the class works correctly. For the purposes of unit testing, I don't really care how the method achieves the result, as long as it's the right one. I don't care if the method figures out the result in-line, or by calling external methods or through internal private methods. If I call calculator.add( 2, 2 ) and get 4 then I'm happy. If I get 5, there's a problem.

In fact, if we're being good developers and remembering to re-factor our code as the application evolves, then there's a good chance that the method we use to determine that result is going to change over the application's lifetime. That gets back to reason #4 for having unit tests - they provide a safety net to ensure that our modules continue to function as we make changes to them. If those unit tests are dependent on how we provide results, then it means that every time we decide to change our class's inner workings, we have to update our unit tests. And if we have to rewrite our unit tests, then what is happening to our safety net?
"being able to quickly create, move around, and change the functionality of private methods is vital to remaining agile" -- Charles Miller, The Fishbowl
Okay, sure if we change our public interfaces, we have to update our unit tests. But that makes sense. It means our interface specifications have changed and so our tests better be updated accordingly. Changing our public interfaces should incur a significant cost because other code relies on them. In fact, one might even argue that having high costs (such as having to update our unit tests) provides a good reminder to us that it's in our best interest to keep our public interfaces stable.
On the other hand, the private parts of our implementations - those details that have been hidden behind our class's interface - should not be costly to change. If we give them the high cost of having to redo our unit tests then it puts up a barrier against refactoring that we really don't want. If we find that we're using the same logic in 3 different places in our class, we should be able to easily move that logic into a single private method and then simply re-run our unit tests as they already exist to make sure our class continues to function as expected. We shouldn't have to go write another unit test at this point because we haven't added any new functionality. We've simply refactored our existing code to be more maintainable.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)






Comments
Kevin Conaway replied on Sat, 2008/07/19 - 8:41am
Well said.
You made a nice case as to why its important to test the result of calling your class, not how your class computed that result. That is an easy trap to fall into when testing with mock objects, especially when mocking the Servlet API
Abby Fichtner replied on Sun, 2008/07/20 - 1:17pm
Thanks for your comment, Kevin. I must admit I'm a bit surprised that I haven't heard from anyone using mocks as to why they're the right way to go even if they do couple your tests with your implementation.
Anyone out there using mock objects that can speak to the other side of this argument?
Kevin Conaway replied on Sun, 2008/07/20 - 1:48pm
in response to:
Abby Fichtner
I must admit I'm a bit surprised that I haven't heard from anyone using mocks as to why they're the right way to go even if they do couple your tests with your implementation.
[/quote]
I don't know that Mocks are the right way so much as sometimes they're the only way.
Years ago when people would test their servlets or Struts actions, you had to interact with the HttpSession or HttpServletRequest at some point. The choices were:
1.) Test nothing
2.) Refactor away your code that touches the Servlet API
3.) Mock the Servlet API
Taking option 2 will eventually bring you back to a choice between 1 and 3.
J Szy replied on Mon, 2008/07/21 - 5:46am
in response to:
Abby Fichtner
Thanks for your comment, Kevin. I must admit I'm a bit surprised that I haven't heard from anyone using mocks as to why they're the right way to go even if they do couple your tests with your implementation.
Anyone out there using mock objects that can speak to the other side of this argument?
[/quote] You should just verify what is important. If you supply a MockStudent, method call order is probably irrelevant, but if it's, say, a MockTransaction, you want to make sure that the transaction is committed after all data is written out and only then.