简体   繁体   English

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

[英]Rspec, Rails: how to test private methods of controllers?

I have controller: 我有控制器:

class AccountController < ApplicationController
  def index
  end

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

How to test private method current_account with rspec? 如何使用rspec测试私有方法current_account

PS I use Rspec2 and Ruby on Rails 3 PS我使用Rspec2和Ruby on Rails 3

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

I use send method. 我用send方法。 Eg: 例如:

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

Because "send" can call private methods 因为“发送”可以调用私有方法

Where is the current_account method being used? current_account方法在哪里使用? What purpose does it serve? 它有什么用途?

Generally, you don't test private methods but rather test the methods that call the private one. 通常,您不测试私有方法,而是测试调用私有方法的方法。

You should not test your private methods directly, they can and should be tested indirectly by exercising the code from public methods. 应该直接测试你的私有方法,他们能够而且应该通过公共方法行使代码间接测试。

This allows you to change the internals of your code down the road without having to change your tests. 这使您可以在不必更改测试的情况下更改代码的内部。

You could make you private or protected methods as public: 您可以将私有或受保护的方法公之于众:

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

Just place this code in your testing class substituting your class name. 只需将此代码放在测试类中,替换您的类名。 Include the namespace if applicable. 如果适用,请包括命名空间。

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

Unit testing private methods seems too out of context with the behaviour of the application. 单元测试私有方法似乎与应用程序的行为脱节。

Are you writing your calling code first? 你是在先编写你的主叫代码吗? This code is not called in your example. 您的示例中未调用此代码。

The behaviour is: you want an object loaded from another object. 行为是:您希望从另一个对象加载对象。

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

Why do u want to write the test out of context from the behaviour you should be trying to describe? 为什么你想从你应该描述的行为中脱离上下文编写测试?

Does this code get used in a lot of places? 这段代码是否在很多地方使用过? Need a more generic approach? 需要更通用的方法吗?

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

Use the rspec-context-private gem to temporarily make private methods public within a context. 使用rspec-context-private gem临时在上下文中公开私有方法。

gem 'rspec-context-private'

It works by adding a shared context to your project. 它的工作原理是为项目添加共享上下文。

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

Then, if you pass :private as metadata to a describe block, the private methods will be public within that context. 然后,如果您将:private作为元数据传递给describe块,则私有方法将在该上下文中公开。

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

I know this is kinda hacky, but it works if you want the methods testable by rspec but not visible in prod. 我知道这有点像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

Now when you can run you're spec like so: 现在,当你可以运行时你就像这样:

RAILS_ENV=test bundle exec rspec spec/foo_spec.rb 

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM