Rspec-rails: cookies.delete["cookie"] functionality seems to be broken with Rails 5.0.0beta3 and RSpec 3.4

Created on 14 Mar 2016  路  16Comments  路  Source: rspec/rspec-rails

I have code that does cookies.delete. If I debug it in my actual controller code, they are gone (and it works in the app itself). However, in the spec, the cookies are still there. I'm pretty sure this worked before I upgraded to Rails 5 (and pointed rspec to the master branch). I've tried everything I can think of, but I'm stumped.

Has reproduction case

Most helpful comment

I've just run into this on Rails 5.0.6. During its rewrite, it seems as though ActionController::TestCase forgot how to delete a cookie from the #cookies object (which is a CookieJar instance), unless the request method is set to GET.

In the context of a controller test, there are two instances of CookieJar involved; #request.cookie_jar and #cookies.

After the controller action under test has run, it calls #write on the request's CookieJar, and then #update on the jar in #cookies.

https://github.com/rails/rails/blob/v5.0.6/actionpack/lib/action_controller/test_case.rb#L554

The call to #update only merges new cookies into the jar; it doesn't delete any that ought to be removed.

However, the call to #write updates the "Set-Cookie" HTTP header, which does give us a workaround. When a cookie is being deleted the HTTP response will send a Set-Cookie header with the string "name_of_cookie=; ".

So you could assert that response.headers['Set-Cookie'] =~ /name_of_deleted_cookie=\; / is not nil.

It looks to me like fixing this in the #cookies jar will need some additional logic in the block I linked to above, deleting cookies from the #cookies jar.

This commit (https://github.com/rails/rails/pull/27586/files) did exactly that, but was reverted as it caused other regressions.

I've been calling cookies.update(response.cookies) after any non-GET requests where I need to test the contents of #cookies, which then allows me to test the contents of #cookies successfully.

All 16 comments

@gregblass for issues like this, it's easiest if we have a small minimal rails app we can clone to debug against. Could you do the following:

1) initialise a new rails app with the 5.0.0.beta3 version of rails and commit
2) make as few changes required to show the incorrect behaviour and commit
3) leave a comment in your spec explaining what the behaviour was on rails 4.2 and what you expect it to be on 5.0

Thanks :+1:

@gregblass I'm going to close this. If you can create a clean reproduction case following the steps listed above, we can re-open and debug.

@samphippen, I faced the same problem on Rails 5.0.0.1 and rspec (3.5.2 and 3.6.0.beta1). I'm just implementing a sign out feature, and it fails. Here is my controller code and controller spec.

RSpec.describe SessionsController, type: :controller do
  describe "POST #destroy" do
    it "delete :remember_token in cookies" do
      cookies.signed[:remember_token] = { value: 'remember_token', expires: 2.weeks.from_now }

      post :destroy

      expect(cookies.signed[:remember_token]).to be_nil
      expect(response).to redirect_to(root_path)
    end
  end
end
class SessionsController < ApplicationController
  def destroy
    cookies.delete(:remember_token)
    redirect_to root_path
  end
end

Failures:

1) SessionsController POST #destroy delete :remember_token in cookies
Failure/Error: expect(cookies.signed[:remember_token]).to be_nil

  expected: nil
       got: "remember_token"
# ./spec/controllers/sessions_controller_spec.rb:10:in `block (3 levels) in <top (required)>'

Finished in 0.05609 seconds (files took 5.34 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/controllers/sessions_controller_spec.rb:5 # SessionsController POST #destroy delete :remember_token in cookies

Here is the repo, https://github.com/chamnap/rails_5_cookie_problem. Let's me know if I do anything wrong.

Same error here since I upgraded to rails 5.0.0 and rspec 3.5.2. Did someone succeed to overcome this issue ?

This is related with #1658

@sgrif can we close this now https://github.com/rails/rails/issues/27145 is merged?

FYI: Rails 5.0.1 still faced this issue, as I tested.

Closing for the same reason as #1658

For some reasons, this issue still exists on rails 5.1.5 and rspec-rails 3.7.2, https://github.com/chamnap/rails_5_cookie_problem.

I've just run into this on Rails 5.0.6. During its rewrite, it seems as though ActionController::TestCase forgot how to delete a cookie from the #cookies object (which is a CookieJar instance), unless the request method is set to GET.

In the context of a controller test, there are two instances of CookieJar involved; #request.cookie_jar and #cookies.

After the controller action under test has run, it calls #write on the request's CookieJar, and then #update on the jar in #cookies.

https://github.com/rails/rails/blob/v5.0.6/actionpack/lib/action_controller/test_case.rb#L554

The call to #update only merges new cookies into the jar; it doesn't delete any that ought to be removed.

However, the call to #write updates the "Set-Cookie" HTTP header, which does give us a workaround. When a cookie is being deleted the HTTP response will send a Set-Cookie header with the string "name_of_cookie=; ".

So you could assert that response.headers['Set-Cookie'] =~ /name_of_deleted_cookie=\; / is not nil.

It looks to me like fixing this in the #cookies jar will need some additional logic in the block I linked to above, deleting cookies from the #cookies jar.

This commit (https://github.com/rails/rails/pull/27586/files) did exactly that, but was reverted as it caused other regressions.

I've been calling cookies.update(response.cookies) after any non-GET requests where I need to test the contents of #cookies, which then allows me to test the contents of #cookies successfully.

Encountered this today and @gma comment helped me with this.
The cookies.update(response.cookies) did the work.

Thanks a lot.

I faced this issue today with Rails 5.2(with Spree 3.7).

Before checking cookies content in spec after request, I did cookies.update(response.cookies) and it worked. Thanks @gma and @ganbootyard

Seen again today with Rails 6.0, RSpec 3.8, and rspec-rails 4.0.0.beta2.

@percysnoodle Can you please try with rspec-rails gem 4.0.0.beta3?

@pirj Done - same problem. (I was using beta2 before; I've updated my previous comment).

This is the failing spec, in case I'm doing something wrong:

    controller(ActionController::Base) do
      def index
        if params[:forget]
          cookies.delete("parameter")
        end

        parameter = params[:parameter] || cookies["parameter"]
        if parameter.present?
          cookies["parameter"] = parameter
        else
          cookies.delete("parameter")
        end

        render plain: parameter || "nil"
      end
    end

    it "forgets a remembered parameter" do
      get :index
      expect(response.body).to eq("nil")

      get :index, params: { parameter: "value" }
      expect(response.body).to eq("value")

      get :index
      expect(response.body).to eq("value")

      get :index, params: { forget: true }
      expect(response.body).to eq("nil")

      get :index
      expect(response.body).to eq("nil") # this is where it fails
    end

This is a Rails issue, the code for updating cookies resides in https://github.com/rails/rails/blob/v6.0.0/actionpack/lib/action_controller/test_case.rb I suggest you construct a similar proof of concept in minitest and open it with them

Was this page helpful?
0 / 5 - 0 ratings