Search code examples
unit-testingprivate-methods

Unit tests, private methods and hidden abstraction


I was reading about unit tests, to learn a bit more about that.

It seems that tests private methods shouldn't be the general rule, only some exceptions. I have find this article, in which explains that: https://enterprisecraftsmanship.com/posts/unit-testing-private-methods/

Here it says that if the private method is complex, one option it is create a public class and implement the method here, so it can be tested.

Here is my doubt.

The reason to don't test private methods it is because it is recomended to test only what the client can use from the library.

The reason to use private methods is don't let to client to know or have details about the internal implementation, so it is good idea to keep the library as simple as possible for the client.

But if for test the private method I create a new public class put the method there, now public, am I not giving to the client details about the implementation? In practice, instead of declaring public the private method, a public class is create to put there the public method.

So I guess that I am misunderstanding something, but I don't know what.

In one of the answers of this question: How do you unit test private methods? it is said that one option it is to pass the private method to a public class too, but it doesn't explain more (I guess the ansewer could be much longer).

Thanks.


Solution

  • The reason to don't test private methods it is because it is recomended to test only what the client can use from the library.

    There are a lot of people explaining "the" goals of unit-testing, but in fact they are describing their goals when doing unit-testing. Unit-testing is applied in many different domains and contexts, starting from toy projects but ending in safety-relevant software for nuclear plants, airplanes etc.

    In other words, there is a lot of software developed where the abovementioned recommendation may be fine. But, you can apply unit-testing way beyond that. If you don't want to start with a restricted view to what unit-testing can be about, you might better look at it in the following way: One of the primary goals of testing in general and also for unit-testing is to find bugs (see Myers, Badgett, Sandler: The Art of Software Testing, or, Beizer: Software Testing Techniques, but also many others).

    Taking it as granted that unit-testing is about finding bugs, then you may also want to test the implementation details: Bugs are in the implementation - different implementations of the same functionality have different bugs. Take a simple fibonacci function: It can be implemented as a recursive function, as an iterative function, as a closed expression (Moivre/Binet), using a hand-written lookup-table, using an automatically-generated lookup-table etc. For each of these implementations, the set of likely bugs will differ dramatically.

    Another example is sorting: There is a plethora of sort functions, which from a functionality perspective do the same thing and many even have the same user interface. The IntroSort algorithm is quite interesting with respect to testing because it implements a quicksort, but when it realizes that it runs into a degenerated sort, it falls back to another algorithm (typically heap-sort). Testing an IntroSort means, also to create such a degenerated set of data that forces the algorithm to enter the heap-sort, just to be sure that the potential bugs in the heap-sort part can be found. Looking at the public interface alone, you would never come up with such a test case (at least that would be quite a coincidence).

    Summarized so far: Testing implementation details is by no means bad practice. It comes at a cost: Tests that go into implementation details are certainly more likely to break or become useless when the implementation changes. Therefore, it depends on your project whether finding more bugs is more important than saving test maintenance effort.

    Regarding the possibilities to make private functions accessible for tests but still not make them part of the "official" public interface: @Cubic has explained nicely the difference between a) being public in the technical sense of the visibility rules of the given programming language, and b) belonging to the "official" and documented public API.