Testing private method in Ruby (rspec)

RubyRspec

Ruby Problem Overview


Yes, I know, that testing private methods it's not a good idea (and I read this thread - http://www.ruby-forum.com/topic/197346 - and some others)

But how can I test the following code?

I use xmpp4r. In my public method #listen I start receive jabber messages like so:

def listen
  @client.add_message_callback do |m|
    do_things_with_message(m)
  end
end

private
def do_things_with_message(m)
  #
end

#add_message_callback - runs block, when message come (in different thread)

So, testing #listen method it's difficult and it more testing xmpp4r than my #do_things_with_message

How to do all right and test #do_things_with_message?:) (http://www.ruby-forum.com/topic/197346#859664)

Refactor private methods to a new object essentialy would be as I making them a public (and class with one method - it's senselessly

EDIT: This is more a theoretical question about clean code and the correct tests. In my first link people argue that the test private methods poorly. I don't want cheat with #send, but also I don't see any viable ways to refactor

Ruby Solutions


Solution 1 - Ruby

You can call a private method in ruby using the send method. Something like this:

@my_object = MyObject.new
@my_object.send(:do_things_with_message, some_message)

In a test that would look something like:

it "should do a thing" do
  my_object = MyObject.new
  my_object.send(:do_things_with_message, some_message)
  my_object.thing.should == true
end

Solution 2 - Ruby

Putting aside the question of whether or not you should be testing a private method, it is very possible in Ruby to temporarily make a private method public. Here is what I mean:

# Metaprogrammatical magic to temporarily expose
# a Class' privates (methods).
class Class
  def publicize_methods
    saved_private_instance_methods = self.private_instance_methods
    self.class_eval { public *saved_private_instance_methods }
    yield
    self.class_eval { private *saved_private_instance_methods }
  end
end

You would use publicize_methods like this:

ClassToTest.publicize_methods do
  ...
  do_private_things_with_message(m).should ???
  ...
end

Solution 3 - Ruby

You probably heard this thought in all the resources that you mention, but the proper "theoretical" way to do it would be to test that @client receives add_message_callback, and then indirectly test your private methods with integration tests. The whole point of unit testing is that you can change implementation, and your tests will still pass

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndreyView Question on Stackoverflow
Solution 1 - RubyAndrew HubbsView Answer on Stackoverflow
Solution 2 - RubyburuzaemonView Answer on Stackoverflow
Solution 3 - RubyenthropsView Answer on Stackoverflow