Lumped tweets

Just marks

Should I test private method? No. Then....

Background

I like the following page.

shoulditestprivatemethods.com

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!"

refs

stackify.com