Testing 1o1: Don't test private methods

Testing 1o1: Don't test private methods
Play this article

I can't stress this enough. You should never, NEVER, come to the point where you have to change the access modifier, just to test a function.

Why?

  • private methods can change their signature

  • classes that call those private methods can change their structure

  • private methods come and go, there's no need for your tests to be dependent on them

The only two solutions to this problem are:

  1. change the access modifier to public, because the behavior should be exposed outside the module (really rare case)

  2. or just write a test for the public API that calls the private methods (almost every time the right choice)

Private methods should always be tested implicitly through the public API.

Example

Let's say you have a User model for the following behavior

# test/models/user.rb
class User
  def initialize(role)
    @role = role
  end

  def calculate_payout(hours)
    get_rate * hours
  end

  private

  def get_rate
    return 420 if @role == 'ceo'
    return 69 if @role == 'dev'
    return 13.37 if @role == 'janitor'
  end
end
# Bad practice example 
test 'given role dev has 69 eur rate' do
  user = User.new('dev')
  expect(user['get_rate']()).to_eql 69 # DO NOT DO THIS!
end
# Good practice example
test 'given role ceo has 420 eur rate' do
  user = User.new('ceo')
  expect(user.calculate_payout(10)).to_eql 4200
end

That way the method get_rate will be tested and if the method ever changes its signature, the test will still pass.

Conclusion

Testing private methods explicitly will make your tests brittle. Brittle tests will make your test suite meaningless and ultimately useless. Stick to the public API for testing.

Did you like this post? Make my day by sharing this post with a dev colleague!