Testing private method in Ruby (rspec)
RubyRspecRuby 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