Ruby on rails Rspec,Rails:如何测试控制器的私有方法?

Ruby on rails Rspec,Rails:如何测试控制器的私有方法?,ruby-on-rails,rspec,Ruby On Rails,Rspec,我有一个控制器: class AccountController < ApplicationController def index end private def current_account @current_account ||= current_user.account end end class AccountController

我有一个控制器:

class AccountController < ApplicationController
  def index
  end

  private
  def current_account
    @current_account ||= current_user.account
  end
end
class AccountController
如何使用rspec测试私人方法
活期账户


另外,我使用Rspec2和RubyonRails3,在哪里使用活期账户方法?它有什么用途


通常,您不测试私有方法,而是测试调用私有方法的方法。

如果需要测试私有函数,请创建调用私有函数的公共方法。

使用#实例_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
我使用send方法。例如:

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

因为“send”可以调用私有方法,所以单元测试私有方法似乎与应用程序的行为脱离上下文

你是先写你的呼叫代码吗? 在您的示例中没有调用此代码

行为是:您希望从另一个对象加载一个对象

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
为什么你想根据你的行为断章取义地编写测试 应该试着去描述吗

这个代码在很多地方使用吗? 需要更通用的方法吗

使用临时在上下文中公开私有方法

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
作为元数据传递给
descripe
块,则私有方法将在该上下文中是公共的

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

您可以将私有或受保护的方法设置为公共:

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

只需将此代码放在测试类中,替换类名即可。如果适用,请包含名称空间。

我知道这有点麻烦,但如果您希望方法可以通过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 

您应该而不是直接测试您的私有方法,它们可以而且应该通过使用公共方法中的代码来间接测试



这允许您在不更改测试的情况下更改代码的内部结构。

如果您愿意,也可以说:@controller.send(:current_account)。Ruby允许您使用send调用私有方法,但这并不一定意味着您应该这样做。测试私有方法是通过测试这些方法的公共接口来完成的。这种方法可行,但并不理想。如果该方法位于包含在控制器中的模块中,则会更好。然后它也可以独立于控制器进行测试。尽管这个答案从技术上回答了这个问题,但我反对,因为它违反了测试中的最佳实践。私有方法不应该被测试,仅仅因为Ruby给了你规避方法可见性的能力,并不意味着你应该滥用它。Srdjan Pejic你能详细说明为什么私有方法不应该被测试吗?我认为你对错误的答案投了否决票,或者没有回答这个问题。这个答案是正确的,不应该被否决。如果你不同意测试私人方法的做法,把它放在评论中,因为这是一个好的信息(很多人都这么做了),然后人们可以向上投票,这仍然显示了你的观点,而不必不必要地否决完全有效的答案。理想情况下,应该测试每种方法。我使用了subject.send和subject.instance\u eval,在rspec@Pullets我不同意,您应该测试API的公共方法,它将调用私有方法,正如我最初的回答所说。你应该测试你提供的API,而不是只有你能看到的私有方法。我同意@Ryan Bigg。您不测试私有方法。这会使您无法重构或更改所述方法的实现,即使该更改不会影响代码的公共部分。在编写自动化测试时,请仔细阅读最佳实践。嗯,也许我遗漏了什么。我编写的类的私有方法比公共方法多得多。仅通过公共API进行测试将导致数百个测试的列表,这些测试不反映他们正在测试的代码。根据我的理解,如果你想在单元测试中实现真正的粒度,也应该测试私有方法。若你们想重构代码,单元测试也必须相应地重构。这保证了你的新代码也能像预期的那样工作。我想你的意思是这应该在你的单元测试代码中完成。这本质上就是.instance_eval和.send在一行代码中所做的。(当一个较短的测试具有相同的效果时,谁想编写更长的测试呢?)唉,这是一个rails控制器。方法必须是私有的。感谢您阅读实际问题。您始终可以将私有方法抽象为服务对象中的公共方法,并以这种方式引用它。这样,您就可以只测试公共方法,但仍然保持代码的干燥。这并不能回答您的问题,但私有方法不应该被测试。您的测试应该只关心真实的东西—您的公共API。如果你的公共方法有效,那么他们称之为私人方法也有效。我不同意。测试代码中任何足够复杂的特性都是有价值的。我也不同意。如果您的公共API正常工作,则只能假设您的私有方法按预期工作。但是您的规范可能是巧合。如果私有方法需要测试,最好将私有方法提取到一个可测试的新类中。@RonLugge您是对的。考虑到更多的后见之明和经验,我不同意我三年前的评论如何使用
.send
在私有方法中测试实例变量?
RAILS_ENV=test bundle exec rspec spec/foo_spec.rb