# Setting session variables in an RSpec Rails request spec You are writing a spec with `type: :request`, i.e. an integration spec instead of a controller spec. Integration specs are wrappers around Rails' `ActionDispatch::IntegrationTest` class. I usually write controller tests using this instead of `type: :controller`, mainly because it exercises more of the request and response handling stack. So instead of writing something like `get :index` to start the request, you would write `get books_path` or similar. One of the issues with using `type: :request` is that you lose the ability to set session variables before issuing a request. Integration tests assume that you will always start a test from an initial state, and mutate the state by issuing requests. I think this is fine if your application is self-contained. Where it falls apart is when your server application has complex request flows involving redirections to third party services (eg. single sign on). In this kind of flow, we might want to start the test from a midway state simply because it is far easier to orchestrate a desired state to test a particular branch (eg. error states). Sometimes the requirements of these flows involve the use of sessions so we cannot avoid making it stateless. What to do then? Unfortunately there is no convenient way to set the session variables in integration tests. At the beginning of the test, the session object is undefined. It only gets defined after a request is made. Even then, the session object is read-only -- modifying it will not carry over the changes to the next request. A solution or strategy that I settled on: 1. Make a route that is only mounted in the test environment. 2. The route handler takes parameters from the request and assigns those parameters to the session. 3. The integration test sets session variables by issuing the request to our test-only route. Sample code follows. ## Make a route that is only mounted in the test environment ```ruby # config/routes.rb Rails.application.routes.draw do if Rails.env.test? namespace :test do resource :session, only: %i[create] end end end ``` ## Create the route handler ```ruby # app/controllers/test/sessions_controller.rb module Test class SessionsController < ApplicationController def create vars = params.permit(session_vars: {}) vars[:session_vars].each do |var, value| session[var] = value end head :created end end end ``` ## Set session variables in the integration test ```ruby require 'rails_helper' describe 'some controller or function', type: :request do def set_session(vars = {}) post test_session_path, params: { session_vars: vars } expect(response).to have_http_status(:created) vars.each_key do |var| expect(session[var]).to be_present end end it 'will do something' do set_session(session_var_1: 'foobar', session_var_2: 'something else') # the rest of your test as usual. get some_function_path expect(response).to be_redirect end end ```