steveklabnik / request_store

Per-request global storage for Rack.
https://github.com/steveklabnik/request_store
MIT License
1.47k stars 87 forks source link

RSpec feature spec context and spec server share same RequestStore #57

Closed glennfu closed 6 years ago

glennfu commented 7 years ago
it "should test this thing" do
    RequestStore[:foo] = "bar"
    visit pages_path
    expect(RequestStore[:foo]).to eq("bar")
end

class PagesController < ApplicationController
    def index
        RequestStore[:foo] = "baz"
    end
end

This spec fails. Is there a better way to structure this? The context of the spec is completely outside the middleware of the test server running the controller action. Normally (and by design) you can't access anything about this server except for the response object. The fact that they share the same RequestStore hash is surprising to me!

To add more context about what I'm doing: I use Apartment for tenants to my application. Part of the setup of connecting to one of those tenants sets some RequestStore stuff. In my spec, I'm relying on that structure to do some setup. I want to do one request against a tenant, do more setup, then do another request against the same tenant. I'm expecting that the RequestStore's context from within the spec setup would be unaffected by the spec server being run.

Additionally, if I run it with the :js flag to make it a Selenium spec, that spec DOES pass.

glennfu commented 7 years ago

Here's the workaround I'm using:

module RequestStoreVisitShim
  def visit(*)
    ca = current_account
    super
    ca.connect if ca
  end
end
module RSpec
  module Rails
    module FeatureExampleGroup
      prepend RequestStoreVisitShim
    end
  end
end

Basically to say take the thing that is currently setup before the visit, and put it back after the visit clears it. I don't know of a way to get the spec context itself on a different Thread than the web server portion other than to make it a js/selenium spec.

rosskevin commented 6 years ago

I've got the same issue, albeit testing post for a /sign_in. I suppose I could instrument all those methods, but seems painful. Still worth using this gem though, it makes life much easier.

rosskevin commented 6 years ago

The following is a sample that patches all rack test methods e.g. get | post | head:

module Test
  module Patches
    module ContextRequestStore
      def process(*)
        begin
          # Rack requests are executed on a single thread, so restore the thread local after request is done.
          old_context = Context.current
          super
        ensure
          Context.current = old_context
        end
      end
    end
  end
end

::ActionDispatch::Integration::Session.send(:prepend, ::Test::Patches::ContextRequestStore)

Easier than I thought...

steveklabnik commented 6 years ago

Seems like this sorted itself out :)