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开发者_JS百科.account
end
end
How to test private method current_account
with rspec?
P.S. I use Rspec2 and Ruby on Rails 3
Use #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. Eg:
event.send(:private_method).should == 2
Because "send" can call private methods
Where is the current_account method being used? 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
Use the rspec-context-private gem to temporarily make private methods public within a context.
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.
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.
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
If you need to test a private function create a public method that invokes the private one.
精彩评论