Rspec, Rails : 컨트롤러의 프라이빗 메서드를 테스트하는 방법?


125

컨트롤러가 있습니다.

class AccountController < ApplicationController
  def index
  end

  private
  def current_account
    @current_account ||= current_user.account
  end
end

current_accountrspec으로 개인 메서드를 테스트하는 방법 은 무엇입니까?

추신 : Rspec2와 Ruby on Rails 3을 사용합니다.


8
이것은 당신의 질문에 대답하지 않지만 개인 방법은 테스트되어서는 안됩니다. 테스트는 실제 공개 API 에만 관심을 가져야합니다 . 공용 메서드가 작동하면 호출하는 개인 메서드도 작동합니다.
— Samy Dindane

77
동의하지 않습니다. 코드에서 충분히 복잡한 기능을 테스트하는 것은 가치가 있습니다.
— whitehat101

11
나는 또한 동의하지 않는다. 공용 API가 작동하는 경우 개인 메서드가 예상대로 작동한다고 가정 할 수 있습니다. 그러나 당신의 사양은 우연히 지나갈 수 있습니다.
— Rimian 2014 년

4
private 메서드에 테스트가 필요한 경우 private 메서드를 테스트 가능한 새 클래스로 추출하는 것이 좋습니다.
— Kris

10
@RonLugge 당신이 맞아요. 더 많은 후견과 경험으로 나는 3 년 된 나의 의견에 동의하지 않습니다. :)
— Samy Dindane 2016 년

답변:


196

#instance_eval 사용

@controller = AccountController.new
@controller.instance_eval{ current_account }   # invoke the private method
@controller.instance_eval{ @current_account }.should eql ... # check the value of the instance variable

94
원하는 경우 @ controller.send (: current_account)라고 말할 수도 있습니다.
— Confusion 2011 년

13
Ruby를 사용하면 send로 개인 메서드를 호출 할 수 있지만 반드시 그렇게해야하는 것은 아닙니다. 개인 메서드 테스트는 해당 메서드에 대한 공용 인터페이스 테스트를 통해 수행됩니다. 이 접근 방식은 작동하지만 이상적이지 않습니다. 방법이 컨트롤러에 포함 된 모듈에 있으면 더 좋을 것입니다. 그런 다음 컨트롤러와 독립적으로 테스트 할 수도 있습니다.
— Brian Hogan

9
이 답변은 기술적으로 질문에 대한 답변이지만 테스트의 모범 사례를 위반하기 때문에 반대 투표입니다. 비공개 메소드를 테스트해서는 안됩니다. Ruby가 메소드 가시성을 우회 할 수있는 기능을 제공한다고해서이를 남용해야한다는 의미는 아닙니다.
— Srdjan Pejic 2011 년

24
Srdjan Pejic이 왜 private 메서드를 테스트해서는 안되는지 자세히 설명해 주시겠습니까?
— John Bachir 2011 년

35
나는 당신이 틀린 답변에 반대표를 던지거나 질문에 대답하지 않는 것 같습니다. 이 답변은 정확하며 찬성해서는 안됩니다. 비공개 방법을 테스트하는 관행에 동의하지 않는 경우 댓글에 좋은 정보 (많은 사람들이 그랬던 것처럼)를 입력하면 사람들이 해당 댓글을 찬성 할 수 있으며, 이는 전적으로 유효한 답변을 불필요하게 반대하지 않고도 여전히 당신의 요점을 보여줍니다.
— traday '2013

37

나는 보내기 방법을 사용합니다. 예 :

event.send(:private_method).should == 2

"send"는 개인 메서드를 호출 할 수 있기 때문에


개인 메서드 내에서 인스턴스 변수를 어떻게 테스트 .send합니까?
— the12

23

current_account 메소드는 어디에 사용됩니까? 어떤 목적으로 사용됩니까?

일반적으로 개인 메서드를 테스트하지 않고 개인 메서드를 호출하는 메서드를 테스트합니다.


5
이상적으로는 모든 방법을 테스트해야합니다. 나는 RSpec에 훨씬 성공을 모두 subject.send 및 subject.instance_eval을 사용했다
— 데이비드 W. 키스

7
@Pullets 동의하지 않습니다. 원래 답변에서 알 수 있듯이 비공개 API를 호출 할 API의 공개 메소드를 테스트해야합니다. 자신 만 볼 수있는 비공개 메서드가 아니라 제공하는 API를 테스트해야합니다.
— Ryan Bigg 2011 년

5
@Ryan Bigg에 동의합니다. 개인 메서드는 테스트하지 않습니다. 이렇게하면 해당 변경 사항이 코드의 공개 부분에 영향을주지 않더라도 해당 메서드의 구현을 리팩터링하거나 변경할 수있는 기능이 제거됩니다. 자동화 된 테스트를 작성할 때 모범 사례를 읽어보십시오.
— Srdjan Pejic 2011 년

4
흠, 뭔가 빠진 것 같아요. 내가 작성한 클래스에는 공개 된 것보다 더 많은 비공개 메서드가 있습니다. 공개 API를 통해서만 테스트하면 테스트중인 코드를 미러링하지 않는 수백 개의 테스트 목록이 생성됩니다.
— David W. Keith

9
내 이해에 따르면 단위 테스트에서 실제 세분화를 얻으려면 개인 메서드도 테스트해야합니다. 코드를 리팩터링하려면 단위 테스트도 그에 따라 리팩토링해야합니다. 이렇게하면 새 코드도 예상대로 작동합니다.
— Indika K 2011

7

당신은해야 하지 직접 개인 방법을 테스트, 그들이 할 수있는 공공 방법의 코드를 운동에 의해 간접적으로 테스트해야합니다.

이를 통해 테스트를 변경하지 않고도 코드의 내부를 변경할 수 있습니다.


4

비공개 또는 보호 된 메소드를 공개로 만들 수 있습니다.

MyClass.send(:public, *MyClass.protected_instance_methods) 
MyClass.send(:public, *MyClass.private_instance_methods)

이 코드를 테스트 클래스에 배치하고 클래스 이름을 대체하십시오. 해당되는 경우 네임 스페이스를 포함합니다.


3
require 'spec_helper'

describe AdminsController do 
  it "-current_account should return correct value" do
    class AccountController
      def test_current_account
        current_account           
      end
    end

    account_constroller = AccountController.new
    account_controller.test_current_account.should be_correct             

   end
end

1

단위 테스트 개인 메서드는 응용 프로그램의 동작과 너무 관련이없는 것 같습니다.

먼저 호출 코드를 작성하고 있습니까? 이 코드는 귀하의 예제에서 호출되지 않습니다.

동작은 다른 개체에서 개체를로드하려는 것입니다.

context "When I am logged in"
  let(:user) { create(:user) }
  before { login_as user }

  context "with an account"
    let(:account) { create(:account) }
    before { user.update_attribute :account_id, account.id }

    context "viewing the list of accounts" do
      before { get :index }

      it "should load the current users account" do
        assigns(:current_account).should == account
      end
    end
  end
end

설명하려는 행동의 맥락에서 테스트를 작성하고 싶은 이유는 무엇입니까?

이 코드가 많은 곳에서 사용됩니까? 보다 일반적인 접근 방식이 필요하십니까?

https://www.relishapp.com/rspec/rspec-rails/v/2-8/docs/controller-specs/anonymous-controller


1

rspec-context-private gem 을 사용하여 컨텍스트 내에서 비공개 메서드를 일시적으로 공개합니다.

gem 'rspec-context-private'

프로젝트에 공유 컨텍스트를 추가하여 작동합니다.

RSpec.shared_context 'private', private: true do

  before :all do
    described_class.class_eval do
      @original_private_instance_methods = private_instance_methods
      public *@original_private_instance_methods
    end
  end

  after :all do
    described_class.class_eval do
      private *@original_private_instance_methods
    end
  end

end

그런 다음 :private메타 데이터로 describe블록에 전달하면 해당 컨텍스트 내에서 private 메서드가 공개됩니다.

describe AccountController, :private do
  it 'can test private methods' do
    expect{subject.current_account}.not_to raise_error
  end
end

0

private 함수를 테스트해야하는 경우 private 함수를 호출하는 공용 메서드를 만듭니다.


3
나는 이것이 단위 테스트 코드에서 수행되어야 함을 의미한다고 가정합니다. 그것은 본질적으로 .instance_eval 및 .send가 한 줄의 코드로 수행하는 작업입니다. (누가 짧은 사람이 같은 효과를 가질 때 더 이상 테스트를 작성하고 싶어?)
— 데이비드 W. 키스

3
한숨, 레일스 컨트롤러입니다. 메서드는 비공개 여야합니다. 실제 질문을 읽어 주셔서 감사합니다.
— Michael Johnston

언제든지 서비스 개체의 공용 메서드로 private 메서드를 추상화하고 그런 방식으로 참조 할 수 있습니다. 이렇게하면 공용 메서드 만 테스트 할 수 있지만 코드는 DRY로 유지할 수 있습니다.
— 제이슨

0

나는 이것이 다소 엉망이라는 것을 알고 있지만 rspec에서 테스트 할 수 있지만 prod에서는 보이지 않는 메서드를 원한다면 작동합니다.

class Foo
  def public_method
    #some stuff
  end

  eval('private') unless Rails.env == 'test'

  def testable_private_method
    # You can test me if you set RAILS_ENV=test
  end 
end

이제 실행할 수 있으면 다음과 같은 사양이됩니다.

RAILS_ENV=test bundle exec rspec spec/foo_spec.rb 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.