What's the best way to unit test protected & p

2019-01-20 23:16发布

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 and dogmatically assert that "you should only unit test public methods; if it needs unit testing, it shouldn't be a protected or private method", but I'm not really interested in debating that. I've got several methods that are protected or private for good and valid reasons, these private/protected methods are moderately complex, and the public methods in the class depend upon these protected/private methods functioning correctly, therefore I need a way to test the protected/private methods.

One more thing... I generally put all the methods for a given class in one file, and the unit tests for that class in another file. Ideally, I'd like all the magic to implement this "unit test of protected and private methods" functionality into the unit test file, not the main source file, in order to keep the main source file as simple and straightforward as possible.

16条回答
Anthone
2楼-- · 2019-01-20 23:21

I know I'm late to the party, but don't test private methods....I can't think of a reason to do this. A publicly accessible method is using that private method somewhere, test the public method and the variety of scenarios that would cause that private method to be used. Something goes in, something comes out. Testing private methods is a big no-no, and it makes it much harder to refactor your code later. They are private for a reason.

查看更多
聊天终结者
3楼-- · 2019-01-20 23:21

Here is a general addition to Class which I use. It's a bit more shotgun than only making public the method you are testing, but in most cases it doesn't matter, and it's much more readable.

class Class
  def publicize_methods
    saved_private_instance_methods = self.private_instance_methods
    self.class_eval { public *saved_private_instance_methods }
    begin
      yield
    ensure
      self.class_eval { private *saved_private_instance_methods }
    end
  end
end

MyClass.publicize_methods do
  assert_equal 10, MyClass.new.secret_private_method
end

Using send to access protected/private methods is broken in 1.9, so is not a recommended solution.

查看更多
老娘就宠你
4楼-- · 2019-01-20 23:22

Here's one easy way if you use RSpec:

before(:each) do
  MyClass.send(:public, *MyClass.protected_instance_methods)  
end
查看更多
淡お忘
5楼-- · 2019-01-20 23:22

In Test::Unit framework can write,

MyClass.send(:public, :method_name)

Here "method_name" is private method.

& while calling this method can write,

assert_equal expected, MyClass.instance.method_name(params)
查看更多
一夜七次
6楼-- · 2019-01-20 23:23

To make public all protected and private method for the described class, you can add the following to your spec_helper.rb and not having to touch any of your spec files.

RSpec.configure do |config|
  config.before(:each) do
    described_class.send(:public, *described_class.protected_instance_methods)
    described_class.send(:public, *described_class.private_instance_methods)
  end
end
查看更多
虎瘦雄心在
7楼-- · 2019-01-20 23:23

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.

查看更多
登录 后发表回答