What's the best way to unit test protected & private methods in Ruby?

后端 未结 16 591
被撕碎了的回忆
被撕碎了的回忆 2020-12-12 10:47

What\'s the best way to unit test protected and private methods in Ruby, using the standard Ruby Test::Unit framework?

I\'m sure somebody will pipe up a

相关标签:
16条回答
  • 2020-12-12 11:30

    In order to do this:

    disrespect_privacy @object do |p|
      assert p.private_method
    end
    

    You can implement this in your test_helper file:

    class ActiveSupport::TestCase
      def disrespect_privacy(object_or_class, &block)   # access private methods in a block
        raise ArgumentError, 'Block must be specified' unless block_given?
        yield Disrespect.new(object_or_class)
      end
    
      class Disrespect
        def initialize(object_or_class)
          @object = object_or_class
        end
        def method_missing(method, *args)
          @object.send(method, *args)
        end
      end
    end
    
    0 讨论(0)
  • 2020-12-12 11:33

    You can bypass encapsulation with the send method:

    myobject.send(:method_name, args)
    

    This is a 'feature' of Ruby. :)

    There was internal debate during Ruby 1.9 development which considered having send respect privacy and send! ignore it, but in the end nothing changed in Ruby 1.9. Ignore the comments below discussing send! and breaking things.

    0 讨论(0)
  • 2020-12-12 11:33

    To correct the top answer above: in Ruby 1.9.1, it's Object#send that sends all the messages, and Object#public_send that respects privacy.

    0 讨论(0)
  • 2020-12-12 11:36

    I would probably lean toward using instance_eval(). Before I knew about instance_eval(), however, I would create a derived class in my unit test file. I would then set the private method(s) to be public.

    In the example below, the build_year_range method is private in the PublicationSearch::ISIQuery class. Deriving a new class just for testing purposes allows me to set a method(s) to be public and, therefore, directly testable. Likewise, the derived class exposes an instance variable called 'result' that was previously not exposed.

    # A derived class useful for testing.
    class MockISIQuery < PublicationSearch::ISIQuery
        attr_accessor :result
        public :build_year_range
    end
    

    In my unit test I have a test case which instantiates the MockISIQuery class and directly tests the build_year_range() method.

    0 讨论(0)
提交回复
热议问题