First of all, I'm aware that in python nothing is really private but let's assume that we can stick to using the leading underscore to indicate that something is private.
I have a module consisting of a bunch of private pure functions (marked with an underscore) and a single public one. We can think in the public one as a composition of the private ones. The module looks like the following:
.
.
.
def _h(x):
...
def _g(x):
...
def _f(x):
...
def foo(x):
return ..._h(_g(_f(x)))
My unit test only calls the public one. I have two problems with this test:
- It is too slow for a unit test (the process is CPU intensive)
- Because it covers so much, when the test fails it takes extra-effort to determine what went wrong
I would prefer to unit test the individual private functions separately and leave the test of the composition (the public function) to an integration test. I have two questions in this regard:
- Is this a sensible approach to tackle the issues mentioned above?
- If yes, what is the right way to implement it? If not, are there any good alternatives?
For the second question, one option is to make the functions public. However, I see no reason to do this because no client uses these functions and it is unlikely that they are useful for anything but the public foo
function. Exposing them just for the sake of testing seems not to be adequate (at least my colleagues consider it that way).
The other option is to ignore the underscore convention and test the private functions. I've read that this is usually considered a bad practice because the private functions are considered "implementation details" and can change anytime. Therefore, testing private functions results in brittle tests. However, the behavior of these private functions is rather stable. I would expect changes within these functions, but their input/output is unlikely to change over time. However, I've failed to find an effective convention/mechanism within python to communicate that this would be the case.
You design your private methods to delegate to a replaceable collaborator