8

Sometimes private functions of a module or class are simply yet-to-be-extracted internal units of functionality, which might deserve their own tests. So why not test them? We will write tests for them later on if/when they're extracted. So why not write the tests now, when they're still part of the same file?

To demonstrate:

enter image description here

First, I wrote module_a. Now I want to write tests for it. I would like to test the 'private' function _private_func. I don't understand why I wouldn't write a test for it, if later I might refactor it to its own internal module anyway, and then write tests for it.


Suppose I have a module with the following functions (it could be also be a class):

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuff and _do_more_stuff are 'private' functions of the module.

I understand the idea that we should only test the public interface, not the implementation details. However, here's the thing:

_do_stuff and _do_more_stuff contain the majority of the functionality of the module. Each one of them could be a public function of a different, 'internal' module. But they are not yet evolved and large enough to be extracted to separate files.

So testing these functions feels right because they are important units of functionality. If they were in different modules as public functions, we would have tested them. So why not test them when they're not yet (or ever) extracted to a different file?

Aviv Cohn
  • 21,190
  • 31
  • 118
  • 178
  • 2
    Possible duplicate of [New to TDD. Should I avoid private methods now?](http://softwareengineering.stackexchange.com/questions/135047/new-to-tdd-should-i-avoid-private-methods-now) – gnat Nov 09 '16 at 14:59
  • 2
    ["Private methods are beneficial to unit testing..."](http://softwareengineering.stackexchange.com/a/292090/31260) "...sticking with private method brings me a useful, reliable enhancement in unit tests. In contrast, weakening access limitations "for testability" only gives me an obscure, hard to understand piece of test code, which is additionally at permanent risk of being broken by any minor refactoring; frankly what I get looks suspiciously like technical debt" – gnat Nov 09 '16 at 15:00
  • 3
    "Should I test private functions?" No. Never, ever, ever. – David Arno Nov 09 '16 at 15:08
  • 2
    @DavidArno Why? What's the alternative to testing internals? Only integration tests? Or making more things public? (though in my experience I mostly test public methods on internal classes, not private methods) – CodesInChaos Nov 09 '16 at 15:13
  • @CodesInChaos, I've answered that with a detailed answer below. – David Arno Nov 09 '16 at 15:18
  • @DavidArno It's still not clear to me if you advocate replacing those unit tests by integration tests or if you advocate exposing a larger API surface. – CodesInChaos Nov 09 '16 at 15:20
  • @CodesInChaos, I'm confused by your question. Whether it's a unit test or integration test depends on the real-world implementation of `public_func` and whether it has side-effects or not. – David Arno Nov 09 '16 at 15:22
  • I have edited my question to clarify. – Aviv Cohn Nov 09 '16 at 15:23
  • @gnat Please re-read the question, I have edited to clarify :) I believe it's a unique question. – Aviv Cohn Nov 09 '16 at 15:29
  • @DavidArno Consider a traditional compiler. It has a minimal API: Sourcecode+options in, binary executable out. But it will have a rich internal representation and functions that analyze or transform it. You can then either expose those internal functions/classes, which means you'll break 3rd party code whenever you refactor or you can restrict your tests to tests that compile full programs and verify their behaviour. Or simply write tests that use those internals. – CodesInChaos Nov 09 '16 at 15:37
  • @CodesInChaos: I am willing to treat compilers as an exception to the usual "enterprise architecture" mindset. – Robert Harvey Nov 09 '16 at 16:22
  • @RobertHarvey Most non-trivial libraries should be the same. It's slightly different for applications, because the `public` keyword doesn't expose a function to 3rd parties and thus doesn't really make it public. For example in a typical SAAS web API, only the web API is effectively public. I generally go by "If I can update/fix all consumers when I change the function's contract it's internal." regardless of which keyword annotates it. – CodesInChaos Nov 09 '16 at 16:31
  • 1
    If it's important enough that there is a need to write tests for it, then it should already be in a separate module. If it's not, then you test its behaviour by using the public API. – Vincent Savard Nov 09 '16 at 17:58
  • 1
    @VincentSavard: If it's not important enough to write unit tests for, it's not important enough to write code for. – Robert Harvey Nov 09 '16 at 20:08
  • 1
    @RobertHarvey Obviously, but I meant in the context of extracting the code to another module. If you can't test it through the public API because it is too complex (as the OP seems to state), then it should already be in another module. Otherwise, if it's actually part of the unit being tested, then it's tested anyway through the public API. – Vincent Savard Nov 09 '16 at 21:12

3 Answers3

14

The need to test is not the same as the need to be public.

Non trivial code needs testing regardless of exposure. Non public behavior doesn't need to exist let alone be tested.

These conflicting views can lead you to want to make every function public or refuse to factor out code into a function unless it will be public.

This is not the answer. Be willing to create private helper functions. Test them through the public interface that uses it as much as possible.

If testing through the public interface doesn't exercise the private function sufficiently the private function is trying to allow to much.

Validation can help narrow what the private function allows. If you can't pass a null going through the public interface you can still throw an exception if one comes through anyway.

Why should you? Why test what you'll never see? Because things change. It may be private now but be public later. The calling code could change. Code that explicitly rejects null makes proper usage and expected state clear.

Of course null could be fine. It's just an example here. But if you expect something, it's useful make that expectation clear.

That might not be the kind of testing you had in mind but hopefully you'll be willing to create private helper functions when appropriate.

The desire to test is good but it shouldn't be the driving force in the design of your public API. Design the public API to be easy to use. It likely won't be if every function is public. The API should be something people can understand how to use without diving into the code. Don't leave such people wondering what this weird helper function is for.

Hiding public helper functions in an internal module is an attempt to respect the need for a clean API while exposing helpers for testing. I won't say this is wrong. You might be taking the first step towards a different architectural layer. But please, master the art of testing private helper functions through the public functions that use them first. That way you won't over use this workaround.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • I've come up with an approach, I'd like to hear your opinion: whenever I'm in a situation where I want to test a private function, I'll check if I can test it sufficiently through one of the public functions (including all edge cases, etc). If I can, I will not write a test for this function specifically, but only test it through testing the public functions which use it. However if I feel the function can't be tested sufficiently through the public interface and does deserve a test of its own, I will extract it to an internal module where its public, and write tests for it. What do you think? – Aviv Cohn Nov 11 '16 at 16:22
  • I'll say I'm asking the same thing the other guys who replied here :) I'd like to hear everybody's opinion. – Aviv Cohn Nov 11 '16 at 16:25
  • Again, I won't tell you no. I am concerned that you've said nothing about keeping an eye on how that impacts usability. The difference between public and private isn't structural. It's usage. If the difference between public and private is a front door and a back door then your work around is to build a shed in the back yard. Fine. So long as people don't get lost back there. – candied_orange Nov 11 '16 at 19:00
  • 1
    Upvoted for "If testing through the public interface doesn't exercise the private function sufficiently the private function is trying to allow to much." – Kris Welsh Nov 14 '16 at 18:34
7

Short answer: No

Longer answer: Yes, but via the public 'API' of your class

The whole idea of private members of a class is that they represent functionality that should be invisible outside the 'unit' of code, however big you want to define that unit to be. In object oriented code that unit often ends up being a class.

You should have your class designed such that it is possible to invoke all the private functionality through various combinations of input state. If you find there isnt a relatively straight forward way to do this, its probably hint that your design needs closer attention.


After clarification of the question, this is just a matter of semantics. If the code in question can operate as a separate standalone unit, and is being tested as if it were public code, i cant see any benefit of not moving it into a standalone module. At present, it only serves to confuse future developers (including yourself, in 6 months time), as to why the apparently public code is hidden inside another module.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
richzilla
  • 1,083
  • 7
  • 11
  • Hi, thanks for your answer :) Please reread the question, I have edited to clarify. – Aviv Cohn Nov 09 '16 at 15:27
  • I've come up with an approach, I'd like to hear your opinion: whenever I'm in a situation where I want to test a private function, I'll check if I can test it sufficiently through one of the public functions (including all edge cases, etc). If I can, I will not write a test for this function specifically, but only test it through testing the public functions which use it. However if I feel the function can't be tested sufficiently through the public interface and does deserve a test of its own, I will extract it to an internal module where its public, and write tests for it. What do you think? – Aviv Cohn Nov 11 '16 at 16:25
  • I'll say I'm asking the same thing the other guys who replied here :) I'd like to hear everybody's opinion. – Aviv Cohn Nov 11 '16 at 16:26
5

The whole point of private functions is that they are hidden implementation details that can be changed at will, without changing the public API. For your example code:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

If you have a series of tests that only use public_func, then if you rewrite it to:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

then, as long as the the return result for a particular value of a remains the same, then all your tests will be good. If the return result changes, a test will fail, highlighting the fact that something broke.

This is all a good thing: static public API; well encapsulated inner workings; and robust tests.

However, if you'd written tests for _do_stuff or _do_more_stuff and then made the above change, you'd now have a bunch of broken tests, not because the functionality changed, but because the implementation of that functionality changed. Those tests would need rewriting to work with the new functions, but having got them working, all you'd know is that those tests worked with the new functions. You'd have lost the original tests and so would not know if the behaviour of public_func had changed and so you tests would basically be useless.

This is a bad thing: a changing API; exposed inner workings tightly coupled to tests; and brittle tests that change as soon as changes to the implementation are made.

So no, do not test private functions. Ever.

David Arno
  • 38,972
  • 9
  • 88
  • 121
  • Hello David, thanks for your answer. Please reread my question, I have edited to clarify. – Aviv Cohn Nov 09 '16 at 15:24
  • Meh. There's nothing wrong with testing internal functions. I personally don't write a non-trivial function of any kind unless I can cover it with a couple of unit tests, so banning testing on internal functions would pretty much preclude me from writing internal functions, robbing me of a very useful technique. API's can and do change; when they do, you must change the tests. Refactoring an inner function (and its test) doesn't break the outer function's tests; that's the whole point of having those tests. Bad advice overall. – Robert Harvey Nov 09 '16 at 15:29
  • What you're really arguing is that you shouldn't have private functions. – Robert Harvey Nov 09 '16 at 15:30
  • @RobertHarvey, Interesting, I was about to make the same comment to you: you are the one arguing for no private/internal functions. Any tested function isn't encapsulated so isn't private. Unsurprisingly, I think you utterly wrong. – David Arno Nov 09 '16 at 15:31
  • Yes, unsurprisingly. Software is a unique profession; seldom is *anything* utterly wrong. Sometimes it merely fails to satisfy the pedants (or the requirements). – Robert Harvey Nov 09 '16 at 15:32
  • @AvivCohn, there is no need for me to update my answer after your question update. Internal functions are still non-public and thus implementation details. Do not directly test implementation details. Of course they get tested by testing the public API, but should not be directly tested. – David Arno Nov 09 '16 at 15:33
  • See [here](http://softwareengineering.stackexchange.com/a/135067/1204) for a more nuanced argument. TDD isn't about tying your hands; it's about encouraging better design. – Robert Harvey Nov 09 '16 at 15:37
  • @RobertHarvey I'm not one for blind dogmatism, but I only imagine two scenarios in practice: 1) the private parts are cohesive and there's not much difference between testing the public class and the private function; or 2) the public class is large enough to be broken up into multiple loosely-coupled components so you might as well make them public and test them separately. The original class is free to stop using those components in the future if its needs change. I suppose it's possible the components aren't interesting enough for reuse, but I see no harm in making them public. – Doval Nov 09 '16 at 15:54
  • @Doval The 'harm' I see in making these private functions public in their own internal modules, is doing so too soon, just so testing them won't be 'testing private functions', and having too many small files. I rather extract things only when they become big enough. Do you advocate extracting such functions in early? – Aviv Cohn Nov 09 '16 at 16:10
  • 1
    @AvivCohn They're either big enough to warrant testing, in which case they're big enough to get their own file; or they're small enough that you don't need to test them separately. So which is it? – Doval Nov 09 '16 at 16:12
  • @Doval: Which basically makes the argument "don't use private methods." – Robert Harvey Nov 09 '16 at 16:21
  • 3
    @RobertHarvey No, it makes the argument "break down big classes into loosely-coupled components if necessary". If they're *really* not public that's probably a good use-case for package-private visibility. – Doval Nov 09 '16 at 16:30
  • @Doval: Ah, so there *is* a way to test such methods and still keep them out of the public API. – Robert Harvey Nov 09 '16 at 16:34
  • Isn't the problem here really that you've got private methods that mutate state? That's what's making them hard to test. If they don't change state, then there's no reason to "hide" them by making them private in the first place. – Graham Nov 10 '16 at 15:33