Background
I like the following page.
It have only contents of "No".
I normally agree for that's saying "no".
But sometime we'd say "it depends on context/case by case" without argue.
Because it certainly depends on the context, and software has a wide variety of backgrounds, and business purposes.
However, I would like to saying "we should avoid write test for private method at anytime as possible as can".
Therefore, I make a proposal for resolution of avoid to testing at private method.
Issue
First of all, Why does we facing thing of would we want to test private method?
Most of the time, there is a complex logic hidden in private method.
And it's often a Public Method argument, or the conditional branch that depends on the instance variables.
For example
class Registration def create(user, params) user = modified_user(user, params.except(:user)) # insert account and update user table Account.save(user, params.except(:account)) end private def modified_user(user, params) # the complex if statements depends on user and params end end
At this point, I'd want to test what account.user
value in the DB.
RSpec.describe Registration do describe "when params.user.x was ..." describe "when params.user.y was ..." describe "when params.account.X was ..." # ... end
This is more than enough. However, the User information in particular is a test of how the Private Method works.
This kind of test will break quickly.
Because Private Method is a class-internal entity and the interface is only loosely bound.
Then
How avoid it? I'd say we should do dependency injection.
In before a day ago, I wrote the what's the constructor injection and what the profit for it. let read if you don't know the dependency injection.
Dependency Injection allows you to inject dependent logic and behavior from the outside and delegate the responsibility of the things that depend on.
And you should do the following.
- Give a name to the behavior that was originally the private method and generate it as a new separate small class.
- Inject a new small class to original big class at construction.
- Instead to call dependency class function from calling private method at the original class.
- Test the Public Method of a small separated class.
For example
class Registration::User def modify(user, params) // complexy work end end class Registration def initialize(user_modifier) @user_modifier = user_modifier end def create(user, params) Account.save(@user_modifier.modify(user, params), params.except(:account)) end end
You can write test code for each Registration::user.modify
and Registration.create
both.
The advantage of this way is that combines responsibility and logic into a small class.
It makes it easier to write test codes.
Also, although I wrote Ruby this time, if you are using PHP or other more typical languages, you can use function type constraints to make the interface fixed.
Refactoring is also easier because the tests are small and easier to write, and if you find out that the changes depend on a class, you only need to fix that part.
I like this small class principle.
Conclusion
I will ever say "Should I test private method? NO!"