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

Ruby on-RailsRspec

Ruby on-Rails Problem Overview


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?

P.S. I use Rspec2 and Ruby on Rails 3

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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

Solution 2 - Ruby on-Rails

I use send method. Eg:

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

Because "send" can call private methods

Solution 3 - Ruby on-Rails

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.

Solution 4 - Ruby on-Rails

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.

Solution 5 - Ruby on-Rails

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.

Solution 6 - Ruby on-Rails

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

Solution 7 - Ruby on-Rails

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

Solution 8 - Ruby on-Rails

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

Solution 9 - Ruby on-Rails

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 

Solution 10 - Ruby on-Rails

If you need to test a private function create a public method that invokes the private one.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpetRUShkaView Question on Stackoverflow
Solution 1 - Ruby on-RailsmonocleView Answer on Stackoverflow
Solution 2 - Ruby on-RailsgraffzonView Answer on Stackoverflow
Solution 3 - Ruby on-RailsRyan BiggView Answer on Stackoverflow
Solution 4 - Ruby on-RailsX.CreatesView Answer on Stackoverflow
Solution 5 - Ruby on-RailszisheView Answer on Stackoverflow
Solution 6 - Ruby on-RailsspeedingdeerView Answer on Stackoverflow
Solution 7 - Ruby on-RailsBrent GreeffView Answer on Stackoverflow
Solution 8 - Ruby on-RailsbarelyknownView Answer on Stackoverflow
Solution 9 - Ruby on-RailsonetwopunchView Answer on Stackoverflow
Solution 10 - Ruby on-RailsliamfrielView Answer on Stackoverflow