0

When I find myself wanting to test the private functions of a class that has a small public API and a complex internal call structure I seem to end up choosing from the two following approaches:

  • If the class has functionality that is not reliant on the class' state and would offer useful functionality to other potential client code then I should break it out into a service and test it's public API.
  • If the class has functionality that is reliant on class' state and would be tightly coupled if broken out then I should test it through the public API by passing the correct parameters and then name the test so that it references the private function I am targeting.

I feel that testing private functions directly makes classes less easy to refactor and tests more brittle but testing private functions through the public API and binding them just by name and correct parameter values also feels a bit shoddy.

Is there a set of rules to abide by in these situations short of doing proper TDD? I have no choice as I am writing tests in retrospect.

hally9k
  • 2,423
  • 2
  • 25
  • 47
  • I would argue that it is not a code smell. This related question (possible duplicate) has a good discussion on the subject: https://stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones – VinGarcia Oct 03 '17 at 17:36

1 Answers1

2

You don't care about private methods being tested. You only care about the public API being tested. For a private method, all that matters about it is how it affects the visible behaviour of the object.

If you have some behaviour of your public API which is implemented as a private method, then that test will likely 'target' the private method through the arguments to the API method. That's not a BadThing per-se - it covers a genuine test case for a real behaviour of the public API. You might, at a later stage, decide to refactor your class in such a way that the same behaviour is implemented in some other fashion. What's important is that the test encapsulates the behaviour of the public API.

It may then happen that you extract the private method into its own class and the method becomes a part of the public API of the new class. That's fine too. Your new class becomes a dependency of the old one, and you can then use DI to decouple the intricacies of triggering the behaviour of the dependency from the actual test case. That too is good, provided the new class has a definite reason for existing beyond just servicing the first class.

It all boils down to what's the best thing for the code you're looking at - does it make sense to completely capture the behaviour of your original API without having to fiddle with mocked dependencies? In an ideal world this would probably always be the case, but as your API becomes larger or more complex or higher-level that can cause problems of its own.

The important thing to remember though is that your tests are never testing a private method. They're always testing a visible behaviour of the system under test, which may (or may not) be implemented in terms of one or more private methods.

sisyphus
  • 6,174
  • 1
  • 25
  • 35