Search code examples
ruby-on-railssessionminitest

How to enable Rails.cache session object for Rails app during tests


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?


Solution

  • 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.