[ruby] Ruby에서 보호 및 개인 메소드를 단위 테스트하는 가장 좋은 방법은 무엇입니까?

표준 Ruby Test::Unit프레임 워크를 사용하여 Ruby에서 보호 및 개인 메소드를 단위 테스트하는 가장 좋은 방법은 무엇입니까 ?

누군가 공개적으로 분석 할 때는 “공용 메소드 만 단위 테스트해야합니다. 단위 테스트가 필요한 경우 보호 된 방법이나 비공개 방법이 아니어야합니다”라고 확신하지만 실제로는 그 논쟁에 관심이 없습니다. 나는 여러 가지 방법을 가지고 있습니다 보호 또는 좋은 유효한 이유로 개인을, 그러므로 내가 테스트하는 방법이 필요합니다, 이러한 개인 / 보호 방법은 적당히 복잡하고, 클래스의 public 메소드가 제대로 작동이 보호 / 개인 방법에 의존 보호 / 비공개 방법

한가지 더 … 나는 일반적으로 주어진 클래스에 대한 모든 메소드를 하나의 파일에 넣고, 유닛은 그 클래스에 대한 다른 파일을 테스트합니다. 이상적으로는 주 소스 파일을 가능한 한 간단하고 간단하게 유지하기 위해이 “보호 및 개인 메소드의 단위 테스트”기능을 주 소스 파일이 아닌 단위 테스트 파일에 구현하는 것이 가장 좋습니다.



답변

send 메소드를 사용하여 캡슐화를 우회 할 수 있습니다.

myobject.send(:method_name, args)

이것은 루비의 ‘기능’입니다. 🙂

루비 1.9 개발 과정에서 send프라이버시 를 존중하고 send!무시 하는 것을 고려한 내부 논쟁이 있었지만, 결국 루비 1.9에서는 아무것도 바뀌지 않았습니다. 아래 내용을 논의 send!하고 깨뜨리는 의견은 무시하십시오 .


답변

RSpec을 사용하는 쉬운 방법은 다음과 같습니다.

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


답변

테스트 파일에서 클래스를 다시 열고 메소드를 공개로 다시 정의하십시오. 메소드 자체의 내장을 재정의 할 필요는 없으며 심볼을 public호출에 전달하면됩니다 .

원래 클래스가 다음과 같이 정의 된 경우 :

class MyClass

  private

  def foo
    true
  end
end

테스트 파일에서 다음과 같이하십시오.

class MyClass
  public :foo

end

public더 많은 개인용 메소드를 노출하려는 경우 여러 기호를 전달할 수 있습니다 .

public :foo, :bar


답변

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

이를 통해 해당 클래스의 다른 객체에 영향을주지 않고 이러한 메소드를 호출 할 수 있습니다. 테스트 디렉토리 내에서 클래스를 다시 열고 테스트 코드 내의 모든 인스턴스에 대해 공개 할 수 있지만 공개 인터페이스 테스트에 영향을 줄 수 있습니다.


답변

과거에 내가 한 일 중 하나는 다음과 같습니다.

class foo
  def public_method
    private_method
  end

private unless 'test' == Rails.env

  def private_method
    'private'
  end
end


답변

누군가 공개적으로 분석 할 때는 “공용 메소드 만 단위 테스트해야합니다. 단위 테스트가 필요한 경우 보호 된 방법이나 비공개 방법이 아니어야합니다”라고 확신하지만 실제로는 그 논쟁에 관심이 없습니다.

또한 메소드를 공용 인 새 객체로 리팩토링하고 원래 클래스에서 비공개로 위임 할 수도 있습니다. 이를 통해 스펙에 매직 메타 루비가없는 메소드를 테스트하면서 비공개로 유지할 수 있습니다.

타당한 이유 때문에 보호되거나 비공개 인 몇 가지 방법이 있습니다.

그 유효한 이유는 무엇입니까? 다른 OOP 언어는 개인 메소드 없이도 얻을 수 있습니다 (개인 메소드는 규칙으로 만 존재하는 스몰 토크가 떠 오릅니다).


답변

@WillSargent의 응답과 마찬가지로, 여기에 FactoryGirl을 사용하여 describe작성 / 업데이트하는 무거운 프로세스를 거치지 않고도 일부 보호 유효성 검사기를 테스트하는 특별한 경우를 위해 블록 에서 사용한 내용 이 있습니다 (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