It's not that you shouldn't test private methods, it's that you should write code in which private methods don't need to be tested.
Of course! I totally agree here, but I also think that we speak too often in absolutes and ignore the nuance that day-to-day coding requires. Arguments like these are sometimes easier said than done.
Anything taken to extremes is unproductive. Including criticism of methodologies based on their philosophies being taken to extremes, usually out of context. Which I find happens with TDD a lot.
As a matter of practice, I test drive the code that I write. I find it extremely helpful to have coverage around the (package) private methods because it allows me to break up the tests into more concise increments. I also find that when I need to expand the functionality that the process goes much quicker when I have tests around all methods that have business logic.
I do not have much concern around deleting previously written unit tests. If the tests are no longer needed then keeping them around provides no value, in fact it just creates extra noise.
I'm on board with all of this!
Like you, I have no problem with deleting code. I actually enjoy it in a weird way because I'm fairly minimalist in my daily life. That said, I could see why the accumulation of tests could prevent someone from scrapping old code.
Some good arguments have already been made here:
TDD your code. This will make sure your code does not need to test private methods.
In your example, you talked about a main method. That is only the input/output. There should be a class that is free of side effects that. An be tested. Then, there might be a parse and toString method that will be utilized by the main method. Both can also be tested. That why a lot of people value TDD: It leads to a clean and testable code.
Absolutely agree! But, I think TDD can sometimes ignore the fact that there is tons of code out there that doesn’t or can’t follow it.
In this particular case, we had a class that couldn’t be instantiated (no public constructor), so the only way to interact with it was through the main method. The only way (I can think of) to put the solution in a testable state would be to write some public wrapper function that accepted everything but the IO streams. Since that function was never going to be used outside of testing, I didn’t bother, but it would have gotten the job done.
Hmm, why not refactor the class then? If it’s untested legacy code, it is legitimate to test private methods and use things like PowerMock to mock static methods etc. But that should be the first step. Once the tests capture the behavior, you should refactor the class bit by bit and start writing new unit tests until you can throw away the old I/O based tests. I’ve done this once for a rather large code base. It was both tedious and soothing at the same time. After a lot of work, I had a tested code base, deleted 1/3 of the code and the application was 10x as fast. 😅
I am actually 100% on board with working toward eliminating private method tests and whatnot. That said, I guess I’m just critical of rules that we tend to blindly follow as a community.
I see the issue a bit differently. If you have many private methods or private methods that are complex enough to warrant being tested, it's a sign that a new module (or class, function, namespace, etc) is waiting to come to life.
That module would essentially be a private module, not a private method or a set of private methods. A new module is easier to test than a private method because the latter might require setting up unrelated stuff to put it under test, and also, some languages won't let you call private methods.
I usually listen to my unit tests: if the examples for a particular module are in great number, messy, or cumbersome, that's a sign a new module could be extracted. That's a useful code smell to be aware of.
All of that to say you are doing the right thing by testing complex private methods, and that's part of the mindset that makes for a good testing strategy, but I'd consider extracting the private methods into first class modules.
Yeah, I saw this argument a lot when I was researching the topic for this piece. But ultimately, I couldn’t find any battle tested rules on the subject—it’s more of a feeling. In other words, when is a good time to create that new module/class?
Regardless, I’m on board. It’s hard to test a class that has too much functionality, and overly complex methods are usually a code smell. I think these types of problems have to be tackled before worrying about private method testing.
If you have properly architectured your code you wont need to test private methods.
For example if I have a class which is responsible for creating a blog post and inside that class I am doing some validation via private methods (like title > 10 chars, thumbnail is set) & I want to see why my tests are failing to create a post. At this point I should be separating out the validation methods to their own class. Now I have an easily testable class that does not violate the single responsibility principle.
This is another argument I hear a lot, and I don’t think it’s always that simple. Busting out a new class every time you want to test something feels like an anti-pattern. At least from a Java perspective, that could generate a lot of files which forces you to maintain some sort of mental record of what they all do.
That said, I agree with the sentiment. I just don’t think it’s always the best course of action.
Test what you need to test and build it as it needs to be built. Having everything in private methods sounds like a Java problem.
To some extent, it definitely is a Java problem. For instance, nothing is really stopping you from testing private Python methods. Meanwhile, one of the only ways to test a Java private method is through reflection.
That said, private methods are also a consequence of philosophical ideas like encapsulation and abstraction. In other words, we like to hide behavior to protect our users from making mistakes. As a result, testing becomes a bit more of a roundabout process.
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.