当前位置:  开发笔记 > 编程语言 > 正文

在Ruby中对受保护和私有方法进行单元测试的最佳方法是什么?

如何解决《在Ruby中对受保护和私有方法进行单元测试的最佳方法是什么?》经验,为你挑选了8个好方法。

使用标准Ruby Test::Unit框架对Ruby中的受保护和私有方法进行单元测试的最佳方法是什么?

我敢肯定有人会说道,"你应该只测试公共方法;如果它需要单元测试,它不应该是一个受保护的或私有的方法",但我真的不想讨论这个问题.我有几个方法保护的或私有的良好和有效的原因,这些私人/受保护的方法是相对复杂的,并且在类的公共方法依赖于这些保护/私有方法正常工作,所以我需要一种方法来测试受保护/私有方法.

还有一件事......我通常将给定类的所有方法放在一个文件中,然后单元在另一个文件中测试该类.理想情况下,我希望将所有"受保护和私有方法的单元测试"功能实现到单元测试文件而不是主要源文件中,以保持主源文件尽可能简单明了.



1> James Baker..:

您可以使用send方法绕过封装:

myobject.send(:method_name, args)

这是Ruby的"特性".:)

Ruby 1.9开发过程中存在内部争论,认为send尊重隐私并send!忽略它,但最终Ruby 1.9没有任何变化.忽略以下评论讨论send!和破坏事情.


我怀疑他们会撤销它,因为他们会立即打破大量的红宝石项目
如果您想尊重隐私,可以使用“ public_send”(文档[here](http://rubydoc.info/stdlib/core/1.9.2/Object:public_send))。我认为这是Ruby 1.9的新功能。

2> Will Sargent..:

如果您使用RSpec,这是一种简单的方法:

before(:each) do
  MyClass.send(:public, *MyClass.protected_instance_methods)  
end


重要警告:这会使此类的方法公开用于测试套件执行的其余部分,这可能会产生意外的副作用!您可能希望在after(:each)块中再次将方法重新定义为受保护,或者在将来遭受怪异的测试失败.
是的,那很好.对于私有方法,请使用... private_instance_methods而不是protected_instance_methods

3> Aaron Hinni..:

只需在测试文件中重新打开该类,然后将这些方法重新定义为公共方法.您不必重新定义方法本身的内容,只需将符号传递给public调用即可.

如果原始类定义如下:

class MyClass

  private

  def foo
    true
  end
end

在您的测试文件中,只需执行以下操作:

class MyClass
  public :foo

end

public如果要公开更多私有方法,可以传递多个符号.

public :foo, :bar



4> rampion..:

instance_eval() 可能有帮助:

--------------------------------------------------- Object#instance_eval
     obj.instance_eval(string [, filename [, lineno]] )   => obj
     obj.instance_eval {| | block }                       => obj
------------------------------------------------------------------------
     Evaluates a string containing Ruby source code, or the given 
     block, within the context of the receiver (obj). In order to set 
     the context, the variable self is set to obj while the code is 
     executing, giving the code access to obj's instance variables. In 
     the version of instance_eval that takes a String, the optional 
     second and third parameters supply a filename and starting line 
     number that are used when reporting compilation errors.

        class Klass
          def initialize
            @secret = 99
          end
        end
        k = Klass.new
        k.instance_eval { @secret }   #=> 99

您可以使用它直接访问私有方法和实例变量.

您也可以考虑使用send(),这也可以让您访问私有和受保护的方法(如James Baker建议)

或者,您可以修改测试对象的元类,以使私有/受保护方法仅为该对象公开.

    test_obj.a_private_method(...) #=> raises NoMethodError
    test_obj.a_protected_method(...) #=> raises NoMethodError
    class << test_obj
        public :a_private_method, :a_protected_method
    end
    test_obj.a_private_method(...) # executes
    test_obj.a_protected_method(...) # executes

    other_test_obj = test.obj.class.new
    other_test_obj.a_private_method(...) #=> raises NoMethodError
    other_test_obj.a_protected_method(...) #=> raises NoMethodError

这将允许您调用这些方法,而不会影响该类的其他对象.您可以在测试目录中重新打开该类,并将其公开用于测试代码中的所有实例,但这可能会影响您对公共接口的测试.



5> Scott..:

我过去做过的一种方法是:

class foo
  def public_method
    private_method
  end

private unless 'test' == Rails.env

  def private_method
    'private'
  end
end



6> 小智..:

我敢肯定有人会说道,"你应该只测试公共方法;如果它需要单元测试,它不应该是一个受保护的或私有的方法",但我真的不想讨论这个问题.

您还可以将这些方法重构为一个新对象,其中这些方法是公共的,并在原始类中私下委托给它们.这将允许您在规范中测试没有魔法元数据的方法,同时保持它们的私密性.

出于良好和有效的原因,我有几种受保护或私有的方法

这些正当理由是什么?其他OOP语言可以在没有私有方法的情况下逃脱(想到smalltalk,其中私有方法仅作为约定存在).



7> 小智..:

要公开所描述的类的所有受保护和私有方法,您可以将以下内容添加到spec_helper.rb,而不必触摸任何spec文件.

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



8> qix..:

与@ WillSargent的响应类似,这是我在一个describe块中用于测试一些受保护验证器的特殊情况,而无需通过FactoryGirl创建/更新它们的重量级过程(并且您可以使用private_instance_methods类似的):

  describe "protected custom `validates` methods" do
    # Test these methods directly to avoid needing FactoryGirl.create
    # to trigger before_create, etc.
    before(:all) do
      @protected_methods = MyClass.protected_instance_methods
      MyClass.send(:public, *@protected_methods)
    end
    after(:all) do
      MyClass.send(:protected, *@protected_methods)
      @protected_methods = nil
    end

    # ...do some tests...
  end

推荐阅读
360691894_8a5c48
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有