At RubyConf today, Stuart Halloway’s Refactotum workshop led to a brief but excellent discussion on the various approaches for testing private methods in Ruby. Ideas ranged from the typical solution of using #send (which won’t work once Ruby 1.9 lands) to Ryan Davis’s technique of simply making everything public. Evan Phoenix, on the other hand, suggested a solution that avoids the soon-to-be-brokeness of using #send while still allowing you to benefit from the inherent intent expressed by defining a method as private.

To demonstrate, let’s assume we have the following (admittedly contrived) class:

class Ninja
  private
    def kill(num_victims)
      "#{num_victims} victims are no longer with us."
    end
end

So how can we make sure that the private method is doing what we want, and do so while testing it in isolation? Why not temporarily define a new public method that simply passes through to our elusive private method?

require 'test/unit'

class NinjaTest < Test::Unit::TestCase
  def test_should_punish_sloppy_coders
    @ninja = Ninja.new
    def @ninja.flog_publicly(*args)
      kill(*args)
    end
    assert_equal '3 victims are no longer with us.', @ninja.flog_publicly(3)
  end
end

Sweet!

$ ruby ninja.rb
Loaded suite ninja
Started
.
Finished in 0.000274 seconds.

1 tests, 1 assertions, 0 failures, 0 errors