Abby Fichtner writes The Hacker Chick Blog, co-organizes Boston's 2500 member Lean Startup Circle, and was Microsoft's Evangelist for Startups where she helped hundreds of startups as they build out the next generation of software. Her background is a mixture of developing bleeding-edge technology for startups and coaching teams on how to improve how they develop software. She's extremely passionate about building communities where innovation thrives and in helping others to push the edge on what’s possible because she believes that each & every one of us is capable of changing the world. Abby is a DZone MVB and is not an employee of DZone and has posted 24 posts at DZone. You can read more from them at their website. View Full User Profile

Your Unit Tests Should Mind Their Own Business

07.16.2008
| 9192 views |
  • submit to reddit

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.

Three unit test scenarios for calculator.add(). Which implementation is most correct?

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.

Published at DZone with permission of Abby Fichtner, author and DZone MVB.

(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

[quote=haxrchick]

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

[quote=haxrchick]

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.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.