Home > Software design >  How to enable Rails.cache session object for Rails app during tests
How to enable Rails.cache session object for Rails app during tests

Time:01-17

I have a multi-step intake form.

I store the data from each step in a Rails.cache object using the session.id as the key.

def initialize_cache
  Rails.cache.fetch(session.id) { Hash.new }
end

def add_params_to_cache
  attrs = Rails.cache.read(session.id).merge(user_profile_params)
  Rails.cache.write(session.id, attrs)
end

This works in development mode.

However, my tests fail because the session.id is nil.

I have enabled caching in the test.rb environment file:

  config.action_controller.perform_caching = true
  config.cache_store = :memory_store

However, I still get nil for session.id.

The only poor solution I have found is to use the following method in the controllers:

    def session_id
  @session_id ||= Rails.env.test? ? :test : session.id
end

Is there a way to get the session object id during rails tests?

CodePudding user response:

You only get session.id when you store something in session, otherwise it's nil. On the first request there is nothing stored in session yet. After your controller action is done, rails stores _csrf_token and so session_id is stored as well. Just take out:

<%= csrf_meta_tags %>

from you layout and session.id will never be initialized (unless you have something else updating session).

Session id is nothing special but you should probably create your own key for this purpose:

# it's what session id is anyway
session[:cache_id] ||= SecureRandom.hex(16)

You can also save something into session and id will initialize:

# assuming there is no session
cookies.clear

session.id #=> nil

# any one of these will initialize session id
session.update({})
session.send(:load!)
session[:touch] = "poke"
# or this
session.destroy
session.clear

session.id #=> "cc3363adc0831258d3173f207a62dcae"

As far as the test is concerned, it's just showing you session.id is not a reliable source for your cache id. Fix it in the controller from the start.

class HomeController < ApplicationController
  def index
    p "this does not initialize session.id"
    p Rails.cache.fetch(session.id) { Hash.new }
    p session.id # still nil
    p
    p "this will initialize session"
    p session.update({}) # or any other way mentioned above
    p Rails.cache.fetch(session.id) { Hash.new }
    p session.id
  end
end
$ bin/rails test test/controllers/home_controller_test.rb
Running 1 tests in a single process (parallelization threshold is 50)
Run options: --seed 48575

# Running:

"this does not initialize session.id"
{}
nil
"this will initialize session"
{"session_id"=>"39c1ee00e1e9214250d0bb6cfbfa68dc"}
{}
"39c1ee00e1e9214250d0bb6cfbfa68dc"
.

Finished in 0.294108s, 3.4001 runs/s, 3.4001 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

This is like visiting the page for the very first time. Try in incognito window, session.id will be nil, but only the first visit. In a test it's always the first visit.

  • Related