But on the other hand: if it's worthwhile testing your private methods, shouldn't they maybe be refactored to a public/package-private scope, so they can be reused?
If you disagree, maybe an illustrative example would help. I couldn't think of one where I want to test a private method in detail that is not worth exposing.
A common case for this is very simple APIs with very complex internals that e.g. dynamically switch behaviors or algorithms in certain contexts. The only way to test all the major code paths through the simple API is to hardcode implementation detail into your unit tests to induce all the various switching contexts indirectly. It makes unit tests quite brittle. At the same time, these internals are definitely not public APIs and unusable as such.
> The only way to test all the major code paths through the simple API is to hardcode implementation detail into your unit tests to induce all the various switching contexts indirectly.
Is the "only way" not property-based testing (and maybe fuzz testing)? If that doesn't get you there, it is likely that the API is poorly designed.
You are assuming the function being tested is more trivial than it actually is. Often these are too small (code-wise) to even have private APIs, the internal behavior of the function is simply and necessarily too complex to unit test in the traditional way.
Some function behavior is intentionally and intrinsically tied to temporal access patterns in the API usage. Unless you can simulate a broad cross-section of real-world runtime API access timing patterns with your testing framework, you won't test all of the code paths. You often see this test problem with scheduler-like functions, where the correct function behavior varies based on internal resource pressures that are an interaction between temporal access patterns of the API and the runtime environment. It is a single function, self-contained, and quite simple, maybe not more than 100 LoC, but test environments are so sterile that usually only a single code path is actually used no matter what you throw at the API.
Some functions have critical code paths that dynamically switch strategies to mitigate when certain types of contention or resource starvation conditions are detected internally. These can be extremely rare cases across the set of possible inputs, such that fuzzing is unlikely to trigger them, or the set of inputs that can trigger them is dependent on exogenous environmental details e.g. the machine where the test is run.
As a fun side-effect, sometimes these functions do not have deterministic result. Getting the same result out of the function all the time does not imply correctness, you also have to know why you got the result you did.
All of the above does not apply to writing business logic in Java or similar. But for high-performance and/or high-reliability systems software, these cases come up often enough that it is a well-understood testing problem. Even if you expose all of the internal implementation detail to the unit tests it is not always possible to reliably trigger all the code paths from an arbitrary test absent purpose-built test tooling.
This is always the theory: "well pull that out into a separate class with a public API". But then those classes end up only being used in one place (the class it was extracted from), with a totally unnecessary increase in public API surface area. Both the extra verbosity and the pointless increase in API surface area are worse than just writing unit tests against private functions IMO.
here's an example - i'm currently working through porting the blossom algorithm [http://jorisvr.nl/article/maximum-matching] to elixir. the algorithm is complex and has a lot of subparts, but the public interface to it is extremely simple. i feel like it's not just useful but almost essential to test a lot of the little parts in isolation so i can make sure they are working correctly.
i am currently marking all functions as public for simplicity, but i feel like it's a failing of elixir and/or its test framework that i cannot say "private, but an associated test module should be able to see them", and once the code is done i will be exploring some of the third-party solutions people have come up with to hack around the issue. (the other option, of course, is to have a new module that just contains a couple of public methods, and regard the entire implementation module as a private module with public functions)
Go comes with this kind of “public” and “private” separation. Only the private tests can access private functions. It also serves to differentiate which tests are for API documentation, which the API must conform to forevermore, and which were used to help with development and can be considered throwaway.
I'm not sure why all testing frameworks don't have this.
A lot of the time _private_ methods on one class end up making more sense as public methods on a helper class of some kind (or public methods on other classes).
One issue that leads to private methods in Java is the lack of any way to extend the set of operations on built-in classes: a problem that Kotlin and C# solve with extension methods.
Extension methods are basically just static methods in a helper class that takes the to be extended class as their first argument, and a bit of syntactic sugar on top. That is absolutely imitable in Java.
If you disagree, maybe an illustrative example would help. I couldn't think of one where I want to test a private method in detail that is not worth exposing.