SebinNyshkim
SebinNyshkim

Reputation: 159

When would I actually be required to unit test private methods?

I'm writing unit tests for the implementation of an API I wrote myself in my company's application. Still new to this whole thing. When looking for answeres on how to unit test certain things I come across a certain pattern. It goes something like this:

Question:

I have this private method I need to unit test.

Top voted answer:

Don't.

I also came across this article arguing against unit testing private methods as well.

Basically how I'm implementing an API I'm given is I write the code first, then I write unit tests to "break it the worst way possible" (as my superior puts it). Once I notice something broke I fix it in the code. To me this seems like a mash-up of OOD and TDD. Is that a legit approach?

The reason I got so many private methods in the first place is that I'm required to break up larger chunks of code into methods. Since these methods are only supposed to be used within the scope of this API implementation I set them to private. Since the file structure established by my team requires me to write all the code into a single file corresponding to an API I can't separate these private methods into a new class and set them to public.

My superior expects me to test these private methods as well. But I'm beginning to doubt if this is even really necessary if the Asserts on the public methods all run successfully?

From my point of view, if my tests on the public methods return the values I expected, I infer that my private methods also work like I intended.

Or am I missing something?

Upvotes: 1

Views: 300

Answers (2)

Fabio Salvalai
Fabio Salvalai

Reputation: 2509

The way you are splitting your code at the moment is out of necessity. You are delegating some work in a private method, because, well, other public methods need to re-use this, and you don't want to copy-paste that code. Of course, since these methods don't make sense being used as standalone methods, you keep them private.

Good, at least you're true to the DRY (Don't Repeat Yourself) principle.

Now, another way to look it is that you want to separate your private methods from the rest of the code, because you want to have a Separation of Concerns. If you do this, you will see that these private methods, although they can't be used on their own, don't really belong to the class containing your public methods, because they don't solve the same concern : This is the Single Responsibility principle: the S in SOLID.

Instead of having your private method within your class, what you can do is move it to another class (a service as I call them), inject it in the class in which they were before, and call these methods instead of the call to the private ones.

Why should you do this ?

  • Because it will be so much easier to test: you delegate a big part of the code, that you will not have to test under a big combination of scenarios.
  • Because you can then inject an alternative implementation (think maintainability: it's easier to replace a brick, than a part of a brick)
  • Because you can delegate the implementation (and the testing) of this service to someone else (you can have 2 developers in parallel working on a very small area of the code)
  • Sometimes, it makes even more sense, because these service classes will then be re-used by other completely different classes that will have the same needs, if they really take care of one single concern.

This last point doesn't always happen, but quite often, it does. I found it is easier to re-use existing data services when they are self-documented: properly-named services and properly-named methods. (your co-workers will discover them more easily)

Now, you don't need to test a private method... because it's public.

You may think it's cheating, because you just made it public, but this comes from a very legitimate approach: Separation of Concerns.

Final notes:

I am convinced your superior is right about asking you to test this code. One thing he could have added was to do that separation into different classes. Also, make sure that you inject these classes using Dependency Injection and Inversion of Control containers. don't instantiate them using the new statement, otherwise, you will not be able to assert that the right method was called with the right arguments !

Upvotes: 1

GhostCat
GhostCat

Reputation: 140427

The core point is: unit tests exist to guarantee that your class under tests behaves as expected.

The behavior of your classes manifests itself via those methods that can be called from "outside" of your classes.

Therefore there is neither need nor sense in trying to directly test private methods.

Of course, it is fair to measure coverage while running unit tests; in order to understand which paths in your code are taken. This information can be used to either enhance test cases (to gain more coverage); or to delete production code (which isn't required).

And to align with your question: you do not use TDD to implement private methods.

You use TDD to create a special form of your "contract" that can be executed automatically. You verify what needs to be done; not how it is actually done in detail. That is especially true since the TDD methodology includes continuous refactoring. You write your tests, you turn them green (by writing production code); and then, at some point, you look into improving the quality of your code. Meaning: you start reworking internal aspects of your class under test. Like: creating more private methods, moving content around; maybe even creating internal-only helper classes and so on. But you keep running your existing tests ... which should still all work; because as said: you write them to check the externally observable behavior (as far as possible).

And beyond that: you should rather looking into "fuzzying" the test data that your unit tests drive into your code instead of worrying about private methods.

What I mean: instead of trying to manually find that test data that makes your production code break, look into concepts like QuickCheck that try to do exactly that automatically.

Final words: and if your management keeps hammering on "test private methods"; then it is your responsibility as engineer to convince them that they are wrong about this. And there is plenty of material out there to back that up.

Upvotes: 6

Related Questions