Tuesday, August 11, 2015

Do you really want to test it?

I’m probably not the only one who had a chance to participate in deliberations over whether some private method should be tested (because it’s worth it) or not (because we shouldn’t test private methods). I’m not writing about pure theoretic conversations while you’re filling your cup of coffee and talking with your colleagues. I believe that in those moments most of developers would tell you a firm NO.

But what about situations when you are working with real code? When all those rules and principles are not so strictly followed? Would they also say NO so firmly? Based on my experience, I can tell you they wouldn’t. At least not always.

Ok, but should they? Should we? Or is it yet another nice-to-follow rule the observance of which depends on context?

I’m sure that I’ve also tested inaccessible methods. I’m also pretty sure that I was certain that’s the right approach. Much better than leaving this piece of code untested or putting lot of effort in testing it through one of the public methods.

I don’t want you to think that I’m saying testing methods that are not the part of an API is something good. Nothing like that. I’ve just written two things:
  • We are certain that a particular functionality needs to be tested for the sake of future maintenance and development.
  • Testing inaccessible methods is not a good idea.
As you can read above, the problem is not testing functionality itself, but its access level.

So what can we do about it?

gimme an access!

The first solution that come to my mind is to change a method’s visibility.
But what about class’ API?
Anyway, all required methods are already public. And the one that we want to test is a result of implementing a body of a different method. In that case, changing access level would result in increasing the number of methods accessible through API. And this is a problem in our case. Bigger API because of tests? I believe that you see that something is not right in here.

When a method is inaccessible in a particular context (as a method of particular class) it is only a part of a functionality. Not a whole, not a unit. Changing its accessibility would make it possible that someone will use it somewhere else. Someone would use a part of a functionality and he wouldn’t even know about it. What’s even worse, he would be sure that the method was designed for it. It’s a part of an API anyway, yes?

Ok, so this is not the solution that we are looking for.

I don’t want it here

And what would you say for a small extraction? Maybe it make sense to take out this private method from the class and move it somewhere else where it won’t be private no longer?

But what’s the difference between this solution and the previous one, you may ask. Finally, we are still ending with a method that can be used somewhere else. And is not that was the reason why previous solution was not satisfying?

This time we’ve also got two things that we need to take into consideration:
  • We want to test a private method, because we think that its complexity is so big that the lack of testing would bring a potential danger ie. failing to find bugs.
  • We cannot change method’s accessibility until it’s placed in current class, because from logical side, it’s only a part of functionality provided by the class.
Ok, so we would like to write Unit Tests for something that is not a Unit. Or, if we want to be more precise, is not a Unit in the context of particular class. That’s why I believe that delegation would be a natural refactoring step.

Yes, we will end up with a method that an inattentive developer can use in a different place. But, in opposition to the previous solution, right now we have a strong dependency and the developer should notice that method is used only by the origin class, only in one place. It should be like a warning sign. It will tell him/her that method is strictly related to it.

So, yes, method will be accessible, but a different approach lets us show dependency and can protect us from incorrect using of it.



So next time when you consider testing a inaccessible method remember that the problem is not your willingness to test. The problem may be caused by not the best design.