Question

I work with simple rails 4.1.0 app and do not use devise. I'm testing application controller:

class ApplicationController < ActionController::Base

  protected

  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end

  def signed_in?
    !!current_user
  end
  helper_method :current_user, :signed_in?

  def current_user=(user)
    @current_user = user
    session[:user_id] = user.try :id
  end

  # want to test this method
  def authenticate_user!
    render nothing: true, status: :unauthorized unless current_user
  end

end

I have a problem with authenticate_user! method. Full spec see here. Method Spec:

  describe 'authenticate_user!' do
    context 'when user logged in' do
      before { subject.send(:current_user=, create(:user)) }

      it 'do nothing' do
        expect(subject.send(:authenticate_user!)).to eq nil
      end
    end

    context 'when user not logged in' do
      controller do
        before_action :authenticate_user!
        def custom
          render :text => 'response'
        end
      end

      before do
        subject.send(:current_user=, nil)
        routes.draw { get 'custom' => 'anonymous#custom' }
        get :custom
      end
      it 'returns unauthorized status' do
        expect(response).to be_nil
      end
    #   before do
    #     subject.send(:current_user=, nil)
    #     subject.send(:authenticate_user!)
    #   end
    #
    #   it { render status: :unauthorized }
    #   it 'renders nothing' do
    #     expect(response).to be_nil
    #   end
    end

  end

It works when user logged in, but when there is no one I tried 2 methods: anonymous controller and simply tried to call this method (commented here). Also i tried to inherit TestApplicationController from ApplicationController and the result the same as with anonymous controller. So in this case error is next:

  Failure/Error: expect(response).to be_nil
   expected: nil
        got: #<ActionController::TestResponse:0x007fc03b158580 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x007fc03b0e3938>, @stream=#<ActionDispatch::Response::Buffer:0x007fc03b08bc10 @response=#<ActionController::TestResponse:0x007fc03b158580 ...>, @buf=[" "], @closed=false>, @header={"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", ....

So it returns some response despite that current_user set to nil and there is before_action :authenticate_user! in controller. I guess it ignores it.

In case of

  before do
    subject.send(:current_user=, nil)
    subject.send(:authenticate_user!)
  end

  it { render status: :unauthorized }
  it 'renders nothing' do
    expect(response).to be_nil
  end

Error is next:

    Failure/Error: subject.send(:authenticate_user!)
 Module::DelegationError:
   ActionController::RackDelegation#status= delegated to @_response.status=, but @_response is nil: #<ApplicationController:0x007fe854c592b8 @_action_has_layout=true, @_routes=nil, @_headers={"Content-Type"=>"text/html"}, @_status=200, @_request=#<ActionController::TestRequest:0x007fe854c78c80 @env={"rack.version"=>[1, 2], "rack.input"=>#<StringIO:0x007fe8544fbf98>, "rack.errors"=>#<StringIO:0x007fe8544f0080>, "rack.multithread"=>true, "rack.multiprocess"=>true, "rack.run_once"=>false, "REQUEST_METHOD"=>"GET", "SERVER_NAME"=>"example.org", "SERVER_PORT"=>"80",

It has no response object to set status.

Also, in this case:

  controller do
    # before_action :authenticate_user!
    def custom
      authenticate_user!
      render :text => 'response'
    end
  end

The error is different:

Failure/Error: render :text => 'response'
 AbstractController::DoubleRenderError:
   Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".

So, render works, but 2 times, and it throws an error.

I found a lot of variety similar tests, but mostly it was devise and methods which i perform.

Thanks for help.

Was it helpful?

Solution

Figured out. The point is - response should not be nil since action answers something (renders and not nil - empty string). And render status: :unauthorized wrong too, there is no render in spec, it's a line from controller (i wonder how it appears there). So the right spec for this method:

context "when user not logged in" do
  controller(ApplicationController) do
    before_action :authenticate_user!
    def custom
      render text: 'response'
    end
  end

  before do
    subject.send(:current_user=, nil)
    routes.draw { get 'custom' => 'anonymous#custom' }
    get 'custom'
  end

  it { should respond_with :unauthorized }
  it "returns nothing" do
    expect(response.body).to be_blank
  end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top