42

Context:

I am currently working on a small project in Python. I commonly structure my classes with some public methods that are documented but mainly deal with the high level concepts (what a user of the class should know and use), and a bunch of hidden (starting with underscore) methods which are in charge of the complex or low level processing.

I know that tests are essential to give confidence in the code and to ensure that any later modification has not broken the previous behaviour.

Problem:

In order to build the higher level public methods on a trusted base, I generally test the private methods. I find it easier to find whether a code modification has introduced regressions and where. It means that those internal tests can breake on minor revisions and will need to be fixed/replaced

But I also know that unit testing private method is at least a disputed concept or more often considered as bad practice. The reason being: only public behaviour should be tested (ref.)

Question:

I do care about following best practices and would like to understand:

  • why is using unit tests on private/hidden methods bad (what is the risk)?
  • what are the best practices when the public methods can use low level and/or complex processing ?

Precisions:

  • it is not a how to question. Python has not true concept of privacy and hidden methods are simply not listed but can be used when you know their name
  • I have never been taught programming rules and patterns: my last classes are from the 80's... I have mainly learned languages by trial and failure and references on Internet (Stack Exchange being my favourite for years)
d219
  • 140

6 Answers6

46

A couple of reasons:

  1. Typically when you're tempted to test a class's private method, it's a design smell (iceberg class, not enough reusable public components, etc). There's almost always some "larger" issue at play.

  2. You can test them through the public interface (which is how you want to test them, because that's how the client will call/use them). You can get a false sense of security by seeing the green light on all the passing tests for your private methods. It is much better/safer to test edge cases on your private functions through your public interface.

  3. You risk severe test duplication (tests that look/feel very similar) by testing private methods. This has major consequences when requirements change, as many more tests than necessary will break. It can also put you in a position where it is hard to refactor because of your test suite...which is the ultimate irony, because the test suite is there to help you safely redesign and refactor!

A tip if you're still tempted to test the private parts (don't use it if it bothers you, and YMMV, but it has worked well for me in the past): Sometimes writing unit tests for private functions just to make sure they're working exactly how you think they are can be valuable (especially if you are new to a language). However, after you're sure they work, delete the tests, and always ensure that the public facing tests are solid and will catch if someone makes an egregious change to said private function.

When to test private methods: Since this answer has gotten (somewhat) popular, I feel obligated to mention that a "best practice" is always just that: a "best practice". It doesn't mean you should do it dogmatically or blindly. If you think you should test your private methods and have a legitimate reason (like you're writing characterization tests for a legacy application), then test your private methods. Specific circumstances always trump any general rule or best practice. Just be aware of some of the things that can go wrong (see above).

I have an answer that goes over this in detail on SO which I'll not repeat here: https://stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones/47401015#47401015

28

Given that one of the main purposes of unit tests is that you can refactor the internals of your program and then be able to verify that you haven't broken its functionality, it's counterproductive if your unit tests operate at such a fine level of granularity that any change to the program code requires you to rewrite your tests.

Pete
  • 3,231
17

Writing unit tests for private methods ties your unit tests to implementation details.

Unit tests should test the behavior of a class at the class's outer surface (it's public API). Unit tests should not have to know anything about the innards of a class. Writing unit tests against a class's implementation details ties your hands when it comes time to refactor. Refactoring is almost certainly going to break those tests, because they're not part of your stable API.

That said, why might you want to write unit tests for your private methods?

There's a natural tension between unit tests and incremental development. Software developers who use a REPL (read-eval-print loop) can attest to how productive it can be to quickly write and test small bits of functionality as you "grow" a class or function. The only good way to do that in environments that are unit test-driven is to write unit tests for private methods, but there's a lot of friction in doing that. Unit tests take time to write, you need an actual method to test against, and your testing framework needs to support the ability to keep the method private so that it doesn't pollute your external API.

Some ecosystems like C# and .NET have ways to create REPL-like environments (tools such as Linqpad do this), but their utility is limited because you don't have access to your project. The immediate window in Visual Studio is inconvenient; it still doesn't have full Intellisense, you have to use fully-qualified names in it, and it triggers a build each time you use it.

Robert Harvey
  • 200,592
7

From my experience I have found that unit testing the internal classes, methods usually means that I have to take the tested functions, classes out. To create another level of abstraction.

This leads to better adherence of the Single Responsiblity Principle.

4

I think this is a good question because it exposes a common problem in testing coverage. But a good answer should tell you that the question is not exactly right because, in theory, you should not be able to unit test private methods. That's why they are private.

Maybe a better question would be "What should I do when i want to test private methods?", and the answer is kind of obvious: you should expose them in a way that makes testing possible. Now, this doesn't necessarily mean that you should just make the method public and that is it. Most likely you'll want to do higher abstraction; move to a different library or API so you can do your tests on that library, without exposing that functionality in your main API.

Remember that there is a reason why your methods have different accessibility levels, and you should always think on how your classes are going to be used in the end.

-1

If testing private methods is not a good practice, then my class should have very simple private methods only (kind of help methods) and public methods (those we can test). Otherwise, and according to the thread of answers, I should probably move some of the complex private methods to an external service and test it by itself which will lead to the exact same number of tests at the end as well as the same impact on what regards eventual future refactoring.

What about if your public methods are complex by nature? Instinctively, I will try to break that public methods into smaller chunks (private or protected methods) and make the implemented business rules testable (usually, I prefer to declare them protected to make them testable) - I know, it is not the same thing as Private but it does the job since I am implementing a service that is exposed via a public interface and therefore, no chances to have classes inherit from it and use, unexpectedly the protected methods.

Finally, if I unit test complex protected/private methods, I ensure that each of them is doing what it intends to do and therefore, the sum of all these methods, in other words, the public methods that use them, will be easiest to debug.

I certainly won't test all private methods. It's much more an exception than I rule - only in case I really need to implement complex business rules in a public method -

So finally.... I read my answer and it looks like the 'It depends'.... because there's no such practice or rule that should be followed blindly. It's all about benefits vs inconvenient balance and this depends on the context and the problem you are trying to solve.