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.