Rails functional testing quirks with Flash and Session

Here’s an interesting behavior I found today. When I say interesting, I mean really, really annoying. It took me all afternoon to find this out.

Here’s a rough idea of my test:

1 get :index
2  assert something
3  get :change_something
4  assert flash[:notice] = "Something changed"

This works fine, but then I made a change to pass session data to the ‘get’ calls – no other code in the system changes

1 get :index, nil, { :user => "foo" }
2  assert something
3  get :change_something, nil, { :user => "foo" }
4  assert flash[:notice] = "Something changed"

Suddenly, the last assert fails. What happened?

With some poking around, I find that ‘flash’ returns an empty hash. Breakpoint really helps to see that the flash is set in my action, but is gone by the time my assert call is made.

Looking much deeper, I find this code in the code to process the HTTP methods

1 @request.session = ActionController::TestSession.new(session) unless session.nil?
2  @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash

If I pass session parameters to my HTTP get, then the entire session is wiped out and re-initialized using the parameters. Also, the flash seems to also be stored in the session object.

When the session is re-initialized, the flash entry is removed, and the Rails flash handling code starts adding the flash entry to the controller’s @flash member instead of the session[‘flash’]

The code that does this is here.

1 @flash = 
2    if @session.is_a?(Hash)
3      FlashHash.new
4    else
5      @session["flash"] ||= FlashHash.new
6    end  

If the flash gets wiped out, a new one is created that, if the session is a plain Hash, is not added to the session.

No errors occur, there is no warning, but the code has changed behavior in a non-obvious way between calls.