ChatGPT解决这个技术问题 Extra ChatGPT

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

我有控制器:

class AccountController < ApplicationController
  def index
  end

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

如何使用 rspec 测试私有方法 current_account

PS 我使用 Rspec2 和 Ruby on Rails 3

这不会回答您的问题,但不应该测试私有方法。你的测试应该只关心真实的东西——你的公共 API。如果您的公共方法有效,那么他们调用的私有方法也有效。
我不同意。测试代码中任何足够复杂的特性都是有价值的。
我也不同意。如果您的公共 API 有效,您只能假设您的私有方法按预期工作。但是您的规格可能是巧合。
如果私有方法需要测试,最好将私有方法提取到可测试的新类中。
@RonLugge 你是对的。有了更多的后见之明和经验,我不同意我三年前的评论。 :)

m
monocle

使用 #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

如果你愿意,你也可以说:@controller.send(:current_account)。
Ruby 允许您使用 send 调用私有方法,但这并不一定意味着您应该这样做。测试私有方法是通过测试这些方法的公共接口来完成的。这种方法可行,但并不理想。如果该方法位于包含在控制器中的模块中会更好。然后它也可以独立于控制器进行测试。
尽管这个答案在技术上回答了这个问题,但我不赞成,因为它违反了测试中的最佳实践。私有方法不应该被测试,仅仅因为 Ruby 让您能够规避方法可见性,这并不意味着您应该滥用它。
Srdjan Pejic 您能否详细说明为什么不应该测试私有方法?
我认为您对错误的答案或不回答问题的答案投了反对票。这个答案是正确的,不应该被否决。如果您不同意测试私有方法的做法,请将其放在评论中,因为它是很好的信息(就像许多人所做的那样),然后人们可以对该评论进行投票,这仍然表明了您的观点,而不会不必要地否决一个完全有效的答案。
P
Perception

我使用发送方法。例如:

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

因为“发送”可以调用私有方法


您将如何使用 .send 测试私有方法中的实例变量?
R
Ryan Bigg

current_account 方法在哪里使用?它的用途是什么?

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


理想情况下,应该测试每种方法。我在 rspec 中使用了 subject.send 和 subject.instance_eval 并取得了很大的成功
@Pullets我不同意,正如我最初的回答所说,您应该测试将调用私有方法的API的公共方法。您应该测试您提供的 API,而不是只有您可以看到的私有方法。
我同意@Ryan Bigg。您不测试私有方法。这消除了您重构或更改所述方法的实现的能力,即使该更改不会影响代码的公共部分。请在编写自动化测试时阅读最佳实践。
嗯,也许我错过了什么。我编写的类有比公共方法更多的私有方法。仅通过公共 API 进行测试会产生数百个测试列表,这些测试不反映他们正在测试的代码。
根据我的理解,如果你想在单元测试中实现真正的粒度,私有方法也应该被测试。如果要重构代码,单元测试也必须相应地重构。这可以确保您的新代码也可以按预期工作。
X
X.Creates

您不应该直接测试您的私有方法,它们可以而且应该通过执行来自公共方法的代码来间接测试。

这使您可以在不更改测试的情况下更改代码的内部结构。


z
zishe

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

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

只需将此代码放在您的测试类中替换您的类名即可。如果适用,包括命名空间。


j
jschorr
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

B
Brent Greeff

单元测试私有方法似乎与应用程序的行为脱节。

你是先写你的调用代码吗?您的示例中未调用此代码。

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

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


b
barelyknown

使用 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 块,则私有方法将在该上下文中公开。

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

o
onetwopunch

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

z
zishe

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


我假设您的意思是这应该在您的单元测试代码中完成。这基本上就是 .instance_eval 和 .send 在一行代码中所做的事情。 (当较短的测试具有相同的效果时,谁愿意编写较长的测试?)
叹息,这是一个轨道控制器。该方法必须是私有的。感谢您阅读实际问题。
您始终可以将私有方法抽象为服务对象中的公共方法并以这种方式引用它。这样你就可以只测试公共方法,但仍然保持你的代码干燥。